agora inbox for [email protected]
help / color / mirror / Atom feedRe: Parallel copy
2431+ messages / 11 participants
[nested] [flat]
* Re: Parallel copy
@ 2020-08-01 04:24 vignesh C <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: vignesh C @ 2020-08-01 04:24 UTC (permalink / raw)
To: Bharath Rupireddy <[email protected]>; +Cc: Amit Kapila <[email protected]>; Ashutosh Sharma <[email protected]>; Rafia Sabih <[email protected]>; Andres Freund <[email protected]>; Robert Haas <[email protected]>; Ants Aasma <[email protected]>; Tomas Vondra <[email protected]>; Alastair Turner <[email protected]>; Thomas Munro <[email protected]>; PostgreSQL Hackers <[email protected]>
The patches were not applying because of the recent commits.
I have rebased the patch over head & attached.
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
On Thu, Jul 23, 2020 at 6:07 PM Bharath Rupireddy <
[email protected]> wrote:
> On Thu, Jul 23, 2020 at 9:22 AM Amit Kapila <[email protected]>
> wrote:
> >
> >>
> >> I ran tests for partitioned use cases - results are similar to that of
> non partitioned cases[1].
> >
> >
> > I could see the gain up to 10-11 times for non-partitioned cases [1],
> can we use similar test case here as well (with one of the indexes on text
> column or having gist index) to see its impact?
> >
> > [1] -
> https://www.postgresql.org/message-id/CALj2ACVR4WE98Per1H7ajosW8vafN16548O2UV8bG3p4D3XnPg%40mail.gma...
> >
>
> Thanks Amit! Please find the results of detailed testing done for
> partitioned use cases:
>
> Range Partitions: consecutive rows go into the same partitions.
> parallel workers test case 1(exec time in sec): copy from csv file, 2
> indexes on integer columns and 1 index on text column, 4 range partitions test
> case 2(exec time in sec): copy from csv file, 1 gist index on text column,
> 4 range partitions test case 3(exec time in sec): copy from csv file, 3
> indexes on integer columns, 4 range partitions
> 0 1051.924(1X) 785.052(1X) 205.403(1X)
> 2 589.576(1.78X) 421.974(1.86X) 114.724(1.79X)
> 4 321.960(3.27X) 230.997(3.4X) 99.017(2.07X)
> 8 199.245(5.23X) *156.132(5.02X)* 99.722(2.06X)
> 16 127.343(8.26X) 173.696(4.52X) 98.147(2.09X)
> 20 *122.029(8.62X)* 186.418(4.21X) 97.723(2.1X)
> 30 142.876(7.36X) 214.598(3.66X) *97.048(2.11X)*
>
> On Thu, Jul 23, 2020 at 10:21 AM Ashutosh Sharma <[email protected]>
> wrote:
> >
> > I think, when doing the performance testing for partitioned table, it
> would be good to also mention about the distribution of data in the input
> file. One possible data distribution could be that we have let's say 100
> tuples in the input file, and every consecutive tuple belongs to a
> different partition.
> >
>
> To address Ashutosh's point, I used hash partitioning. Hope this helps to
> clear the doubt.
>
> Hash Partitions: where there are high chances that consecutive rows may go
> into different partitions.
> parallel workers test case 1(exec time in sec): copy from csv file, 2
> indexes on integer columns and 1 index on text column, 4 hash partitions test
> case 2(exec time in sec): copy from csv file, 1 gist index on text column,
> 4 hash partitions test case 3(exec time in sec): copy from csv file, 3
> indexes on integer columns, 4 hash partitions
> 0 1060.884(1X) 812.283(1X) 207.745(1X)
> 2 572.542(1.85X) 418.454(1.94X) 107.850(1.93X)
> 4 298.132(3.56X) 227.367(3.57X) *83.895(2.48X)*
> 8 169.449(6.26X) 137.993(5.89X) 85.411(2.43X)
> 16 112.297(9.45X) 95.167(8.53X) 96.136(2.16X)
> 20 *101.546(10.45X)* *90.552(8.97X)* 97.066(2.14X)
> 30 113.877(9.32X) 127.17(6.38X) 96.819(2.14X)
>
>
> With Regards,
> Bharath Rupireddy.
> EnterpriseDB: http://www.enterprisedb.com
>
Attachments:
[text/x-patch] v2-0001-Copy-code-readjustment-to-support-parallel-copy.patch (16.6K, ../../CALDaNm2EYd67r3NwScaFh9_onbX_vpKJVS9p-=+TX22q47m+Zg@mail.gmail.com/3-v2-0001-Copy-code-readjustment-to-support-parallel-copy.patch)
download | inline diff:
From f2ba043af005c55961ed68c9a595cee58c46c79d Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 08:52:48 +0530
Subject: [PATCH v2 1/5] Copy code readjustment to support parallel copy.
This patch has the copy code slightly readjusted so that the common code is
separated to functions/macros, these functions/macros will be used by the
workers in the parallel copy code of the upcoming patches. EOL removal is moved
from CopyReadLine to CopyReadLineText, this change was required because in case
of parallel copy the record identification and record updation is done in
CopyReadLineText, before record information is updated in shared memory the new
line characters should be removed.
---
src/backend/commands/copy.c | 361 ++++++++++++++++++++++++++------------------
1 file changed, 218 insertions(+), 143 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index db7d24a..3efafca 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -95,6 +95,9 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
+
/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
@@ -224,7 +227,6 @@ typedef struct CopyStateData
* appropriate amounts of data from this buffer. In both modes, we
* guarantee that there is a \0 at raw_buf[raw_buf_len].
*/
-#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
@@ -354,6 +356,27 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/*
+ * CLEAR_EOL_LINE - Wrapper for clearing EOL.
+ */
+#define CLEAR_EOL_LINE() \
+if (!result && !IsHeaderLine()) \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len) \
+
+/*
+ * INCREMENTPROCESSED - Increment the lines processed.
+ */
+#define INCREMENTPROCESSED(processed) \
+processed++;
+
+/*
+ * GETPROCESSED - Get the lines processed.
+ */
+#define GETPROCESSED(processed) \
+return processed;
+
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -401,7 +424,11 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
-
+static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
+ List *attnamelist);
+static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size);
+static void ConvertToServerEncoding(CopyState cstate);
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -801,14 +828,18 @@ CopyLoadRawBuf(CopyState cstate)
{
int nbytes = RAW_BUF_BYTES(cstate);
int inbytes;
+ int minread = 1;
/* Copy down the unprocessed data if any. */
if (nbytes > 0)
memmove(cstate->raw_buf, cstate->raw_buf + cstate->raw_buf_index,
nbytes);
+ if (cstate->copy_dest == COPY_NEW_FE)
+ minread = RAW_BUF_SIZE - nbytes;
+
inbytes = CopyGetData(cstate, cstate->raw_buf + nbytes,
- 1, RAW_BUF_SIZE - nbytes);
+ minread, RAW_BUF_SIZE - nbytes);
nbytes += inbytes;
cstate->raw_buf[nbytes] = '\0';
cstate->raw_buf_index = 0;
@@ -1514,7 +1545,6 @@ BeginCopy(ParseState *pstate,
{
CopyState cstate;
TupleDesc tupDesc;
- int num_phys_attrs;
MemoryContext oldcontext;
/* Allocate workspace and zero all fields */
@@ -1680,6 +1710,24 @@ BeginCopy(ParseState *pstate,
tupDesc = cstate->queryDesc->tupDesc;
}
+ PopulateCommonCstateInfo(cstate, tupDesc, attnamelist);
+ cstate->copy_dest = COPY_FILE; /* default */
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return cstate;
+}
+
+/*
+ * PopulateCommonCstateInfo - Populates the common variables required for copy
+ * from operation. This is a helper function for BeginCopy function.
+ */
+static void
+PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc,
+ List *attnamelist)
+{
+ int num_phys_attrs;
+
/* Generate or convert list of attributes to process */
cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
@@ -1799,12 +1847,6 @@ BeginCopy(ParseState *pstate,
pg_database_encoding_max_length() > 1);
/* See Multibyte encoding comment above */
cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->file_encoding);
-
- cstate->copy_dest = COPY_FILE; /* default */
-
- MemoryContextSwitchTo(oldcontext);
-
- return cstate;
}
/*
@@ -2696,32 +2738,11 @@ CopyMultiInsertInfoStore(CopyMultiInsertInfo *miinfo, ResultRelInfo *rri,
}
/*
- * Copy FROM file to relation.
+ * Check if the relation specified in copy from is valid.
*/
-uint64
-CopyFrom(CopyState cstate)
+static void
+CheckTargetRelValidity(CopyState cstate)
{
- ResultRelInfo *resultRelInfo;
- ResultRelInfo *target_resultRelInfo;
- ResultRelInfo *prevResultRelInfo = NULL;
- EState *estate = CreateExecutorState(); /* for ExecConstraints() */
- ModifyTableState *mtstate;
- ExprContext *econtext;
- TupleTableSlot *singleslot = NULL;
- MemoryContext oldcontext = CurrentMemoryContext;
-
- PartitionTupleRouting *proute = NULL;
- ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
- int ti_options = 0; /* start with default options for insert */
- BulkInsertState bistate = NULL;
- CopyInsertMethod insertMethod;
- CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
- uint64 processed = 0;
- bool has_before_insert_row_trig;
- bool has_instead_insert_row_trig;
- bool leafpart_use_multi_insert = false;
-
Assert(cstate->rel);
/*
@@ -2758,27 +2779,6 @@ CopyFrom(CopyState cstate)
RelationGetRelationName(cstate->rel))));
}
- /*
- * If the target file is new-in-transaction, we assume that checking FSM
- * for free space is a waste of time. This could possibly be wrong, but
- * it's unlikely.
- */
- if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
- (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
- cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
- ti_options |= TABLE_INSERT_SKIP_FSM;
-
- /*
- * Optimize if new relfilenode was created in this subxact or one of its
- * committed children and we won't see those rows later as part of an
- * earlier scan or command. The subxact test ensures that if this subxact
- * aborts then the frozen rows won't be visible after xact cleanup. Note
- * that the stronger test of exactly which subtransaction created it is
- * crucial for correctness of this optimization. The test for an earlier
- * scan or command tolerates false negatives. FREEZE causes other sessions
- * to see rows they would not see under MVCC, and a false negative merely
- * spreads that anomaly to the current session.
- */
if (cstate->freeze)
{
/*
@@ -2816,9 +2816,61 @@ CopyFrom(CopyState cstate)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot perform COPY FREEZE because the table was not created or truncated in the current subtransaction")));
+ }
+}
+
+/*
+ * Copy FROM file to relation.
+ */
+uint64
+CopyFrom(CopyState cstate)
+{
+ ResultRelInfo *resultRelInfo;
+ ResultRelInfo *target_resultRelInfo;
+ ResultRelInfo *prevResultRelInfo = NULL;
+ EState *estate = CreateExecutorState(); /* for ExecConstraints() */
+ ModifyTableState *mtstate;
+ ExprContext *econtext;
+ TupleTableSlot *singleslot = NULL;
+ MemoryContext oldcontext = CurrentMemoryContext;
+
+ PartitionTupleRouting *proute = NULL;
+ ErrorContextCallback errcallback;
+ CommandId mycid = GetCurrentCommandId(true);
+ int ti_options = 0; /* start with default options for insert */
+ BulkInsertState bistate = NULL;
+ CopyInsertMethod insertMethod;
+ CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
+ uint64 processed = 0;
+ bool has_before_insert_row_trig;
+ bool has_instead_insert_row_trig;
+ bool leafpart_use_multi_insert = false;
+
+ CheckTargetRelValidity(cstate);
+ /*
+ * If the target file is new-in-transaction, we assume that checking FSM
+ * for free space is a waste of time. This could possibly be wrong, but
+ * it's unlikely.
+ */
+ if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
+ (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
+ cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
+ ti_options |= TABLE_INSERT_SKIP_FSM;
+
+ /*
+ * Optimize if new relfilenode was created in this subxact or one of its
+ * committed children and we won't see those rows later as part of an
+ * earlier scan or command. The subxact test ensures that if this subxact
+ * aborts then the frozen rows won't be visible after xact cleanup. Note
+ * that the stronger test of exactly which subtransaction created it is
+ * crucial for correctness of this optimization. The test for an earlier
+ * scan or command tolerates false negatives. FREEZE causes other sessions
+ * to see rows they would not see under MVCC, and a false negative merely
+ * spreads that anomaly to the current session.
+ */
+ if (cstate->freeze)
ti_options |= TABLE_INSERT_FROZEN;
- }
/*
* We need a ResultRelInfo so we can use the regular executor's
@@ -3311,7 +3363,7 @@ CopyFrom(CopyState cstate)
* or FDW; this is the same definition used by nodeModifyTable.c
* for counting tuples inserted by an INSERT command.
*/
- processed++;
+ INCREMENTPROCESSED(processed)
}
}
@@ -3366,30 +3418,15 @@ CopyFrom(CopyState cstate)
FreeExecutorState(estate);
- return processed;
+ GETPROCESSED(processed)
}
/*
- * Setup to read tuples from a file for COPY FROM.
- *
- * 'rel': Used as a template for the tuples
- * 'filename': Name of server-local file to read
- * 'attnamelist': List of char *, columns to include. NIL selects all cols.
- * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
- *
- * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ * PopulateCstateCatalogInfo - populate the catalog information.
*/
-CopyState
-BeginCopyFrom(ParseState *pstate,
- Relation rel,
- const char *filename,
- bool is_program,
- copy_data_source_cb data_source_cb,
- List *attnamelist,
- List *options)
+static void
+PopulateCstateCatalogInfo(CopyState cstate)
{
- CopyState cstate;
- bool pipe = (filename == NULL);
TupleDesc tupDesc;
AttrNumber num_phys_attrs,
num_defaults;
@@ -3399,38 +3436,8 @@ BeginCopyFrom(ParseState *pstate,
Oid in_func_oid;
int *defmap;
ExprState **defexprs;
- MemoryContext oldcontext;
bool volatile_defexprs;
- cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
- oldcontext = MemoryContextSwitchTo(cstate->copycontext);
-
- /* Initialize state variables */
- cstate->reached_eof = false;
- cstate->eol_type = EOL_UNKNOWN;
- cstate->cur_relname = RelationGetRelationName(cstate->rel);
- cstate->cur_lineno = 0;
- cstate->cur_attname = NULL;
- cstate->cur_attval = NULL;
-
- /*
- * Set up variables to avoid per-attribute overhead. attribute_buf and
- * raw_buf are used in both text and binary modes, but we use line_buf
- * only in text mode.
- */
- initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
- cstate->raw_buf_index = cstate->raw_buf_len = 0;
- if (!cstate->binary)
- {
- initStringInfo(&cstate->line_buf);
- cstate->line_buf_converted = false;
- }
-
- /* Assign range table, we'll need it in CopyFrom. */
- if (pstate)
- cstate->range_table = pstate->p_rtable;
-
tupDesc = RelationGetDescr(cstate->rel);
num_phys_attrs = tupDesc->natts;
num_defaults = 0;
@@ -3508,6 +3515,61 @@ BeginCopyFrom(ParseState *pstate,
cstate->defexprs = defexprs;
cstate->volatile_defexprs = volatile_defexprs;
cstate->num_defaults = num_defaults;
+}
+
+/*
+ * Setup to read tuples from a file for COPY FROM.
+ *
+ * 'rel': Used as a template for the tuples
+ * 'filename': Name of server-local file to read
+ * 'attnamelist': List of char *, columns to include. NIL selects all cols.
+ * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
+ *
+ * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ */
+CopyState
+BeginCopyFrom(ParseState *pstate,
+ Relation rel,
+ const char *filename,
+ bool is_program,
+ copy_data_source_cb data_source_cb,
+ List *attnamelist,
+ List *options)
+{
+ CopyState cstate;
+ bool pipe = (filename == NULL);
+ MemoryContext oldcontext;
+
+ cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ /* Initialize state variables */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /*
+ * Set up variables to avoid per-attribute overhead. attribute_buf is
+ * used in both text and binary modes, but we use line_buf and raw_buf
+ * only in text mode.
+ */
+ initStringInfo(&cstate->attribute_buf);
+ cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ if (!cstate->binary)
+ {
+ initStringInfo(&cstate->line_buf);
+ cstate->line_buf_converted = false;
+ }
+
+ /* Assign range table, we'll need it in CopyFrom. */
+ if (pstate)
+ cstate->range_table = pstate->p_rtable;
+
+ PopulateCstateCatalogInfo(cstate);
cstate->is_program = is_program;
if (data_source_cb)
@@ -3917,45 +3979,60 @@ CopyReadLine(CopyState cstate)
} while (CopyLoadRawBuf(cstate));
}
}
- else
- {
- /*
- * If we didn't hit EOF, then we must have transferred the EOL marker
- * to line_buf along with the data. Get rid of it.
- */
- switch (cstate->eol_type)
- {
- case EOL_NL:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CR:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\r');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CRNL:
- Assert(cstate->line_buf.len >= 2);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 2] == '\r');
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len -= 2;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_UNKNOWN:
- /* shouldn't get here */
- Assert(false);
- break;
- }
- }
+ ConvertToServerEncoding(cstate);
+ return result;
+}
+
+/*
+ * ClearEOLFromCopiedData - Clear EOL from the copied data.
+ */
+static void
+ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size)
+{
+ /*
+ * If we didn't hit EOF, then we must have transferred the EOL marker
+ * to line_buf along with the data. Get rid of it.
+ */
+ switch (cstate->eol_type)
+ {
+ case EOL_NL:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CR:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\r');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CRNL:
+ Assert(*copy_line_size >= 2);
+ Assert(copy_line_data[copy_line_pos - 2] == '\r');
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 2] = '\0';
+ *copy_line_size -= 2;
+ break;
+ case EOL_UNKNOWN:
+ /* shouldn't get here */
+ Assert(false);
+ break;
+ }
+}
+
+/*
+ * ConvertToServerEncoding - convert contents to server encoding.
+ */
+static void
+ConvertToServerEncoding(CopyState cstate)
+{
/* Done reading the line. Convert it to server encoding. */
if (cstate->need_transcoding)
{
char *cvt;
-
cvt = pg_any_to_server(cstate->line_buf.data,
cstate->line_buf.len,
cstate->file_encoding);
@@ -3967,11 +4044,8 @@ CopyReadLine(CopyState cstate)
pfree(cvt);
}
}
-
/* Now it's safe to use the buffer in error messages */
cstate->line_buf_converted = true;
-
- return result;
}
/*
@@ -4334,6 +4408,7 @@ not_end_of_copy:
* Transfer any still-uncopied data to line_buf.
*/
REFILL_LINEBUF;
+ CLEAR_EOL_LINE();
return result;
}
--
1.8.3.1
[text/x-patch] v2-0002-Framework-for-leader-worker-in-parallel-copy.patch (31.5K, ../../CALDaNm2EYd67r3NwScaFh9_onbX_vpKJVS9p-=+TX22q47m+Zg@mail.gmail.com/4-v2-0002-Framework-for-leader-worker-in-parallel-copy.patch)
download | inline diff:
From 1bf7d74c7308e6eae807c0258322abbb3890aede Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 08:56:39 +0530
Subject: [PATCH v2 2/5] Framework for leader/worker in parallel copy
This patch has the framework for data structures in parallel copy, leader
initialization, worker initialization, shared memory updation, starting workers,
wait for workers and workers exiting.
---
src/backend/access/transam/parallel.c | 4 +
src/backend/commands/copy.c | 756 +++++++++++++++++++++++++++++++++-
src/include/commands/copy.h | 2 +
src/tools/pgindent/typedefs.list | 7 +
4 files changed, 765 insertions(+), 4 deletions(-)
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index b042696..a3cff4b 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -25,6 +25,7 @@
#include "catalog/pg_enum.h"
#include "catalog/storage.h"
#include "commands/async.h"
+#include "commands/copy.h"
#include "executor/execParallel.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
@@ -145,6 +146,9 @@ static const struct
},
{
"parallel_vacuum_main", parallel_vacuum_main
+ },
+ {
+ "ParallelCopyMain", ParallelCopyMain
}
};
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3efafca..3dcdfc1 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -96,9 +96,180 @@ typedef enum CopyInsertMethod
} CopyInsertMethod;
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+
+/*
+ * The macros DATA_BLOCK_SIZE, RINGSIZE & MAX_BLOCKS_COUNT stores the records
+ * read from the file that need to be inserted into the relation. These values
+ * help in handover of multiple records with significant size of data to be
+ * processed by each of the workers to make sure there is no context switch & the
+ * work is fairly distributed among the workers. This number showed best
+ * results in the performance tests.
+ */
+#define DATA_BLOCK_SIZE RAW_BUF_SIZE
+
+/* It can hold upto 10000 record information for worker to process. */
+#define RINGSIZE (10 * 1000)
+
+/* It can hold 1000 blocks of 64K data in DSM to be processed by the worker. */
+#define MAX_BLOCKS_COUNT 1000
+
+/*
+ * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
+ * block to process to avoid lock contention. This value should be mode of
+ * RINGSIZE, as wrap around cases is currently not handled while selecting the
+ * WORKER_CHUNK_COUNT by the worker.
+ */
+#define WORKER_CHUNK_COUNT 50
+
+#define IsParallelCopy() (cstate->is_parallel)
+#define IsLeader() (cstate->pcdata->is_leader)
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
+ * Copy data block information.
+ * ParallelCopyDataBlock's will be created in DSM. Data read from file will be
+ * copied in these DSM data blocks. The leader process identifies the records
+ * and the record information will be shared to the workers. The workers will
+ * insert the records into the table. There can be one or more number of records
+ * in each of the data block based on the record size.
+ */
+typedef struct ParallelCopyDataBlock
+{
+ /* The number of unprocessed lines in the current block. */
+ pg_atomic_uint32 unprocessed_line_parts;
+
+ /*
+ * If the current line data is continued into another block, following_block
+ * will have the position where the remaining data need to be read.
+ */
+ uint32 following_block;
+
+ /*
+ * This flag will be set, when the leader finds out this block can be read
+ * safely by the worker. This helps the worker to start processing the line
+ * early where the line will be spread across many blocks and the worker
+ * need not wait for the complete line to be processed.
+ */
+ bool curr_blk_completed;
+ char data[DATA_BLOCK_SIZE]; /* data read from file */
+ uint8 skip_bytes;
+}ParallelCopyDataBlock;
+
+/*
+ * Individual line information.
+ * ParallelCopyLineBoundary is common data structure between leader & worker,
+ * Leader process will be populating data block, data block offset & the size of
+ * the record in DSM for the workers to copy the data into the relation.
+ * This is protected by the following sequence in the leader & worker. If they
+ * don't follow this order the worker might process wrong line_size and leader
+ * might populate the information which worker has not yet processed or in the
+ * process of processing.
+ * Leader should operate in the following order:
+ * 1) check if line_size is -1, if not wait, it means worker is still
+ * processing.
+ * 2) set line_state to LINE_LEADER_POPULATING.
+ * 3) update first_block, start_offset & cur_lineno in any order.
+ * 4) update line_size.
+ * 5) update line_state to LINE_LEADER_POPULATED.
+ * Worker should operate in the following order:
+ * 1) check line_state is LINE_LEADER_POPULATED, if not it means leader is still
+ * populating the data.
+ * 2) read line_size.
+ * 3) only one worker should choose one line for processing, this is handled by
+ * using pg_atomic_compare_exchange_u32, worker will change the sate to
+ * LINE_WORKER_PROCESSING only if line_state is LINE_LEADER_POPULATED.
+ * 4) read first_block, start_offset & cur_lineno in any order.
+ * 5) process line_size data.
+ * 6) update line_size to -1.
+ */
+typedef struct ParallelCopyLineBoundary
+{
+ /* Position of the first block in data_blocks array. */
+ uint32 first_block;
+ uint32 start_offset; /* start offset of the line */
+
+ /*
+ * Size of the current line -1 means line is yet to be filled completely,
+ * 0 means empty line, >0 means line filled with line size data.
+ */
+ pg_atomic_uint32 line_size;
+ pg_atomic_uint32 line_state; /* line state */
+ uint64 cur_lineno; /* line number for error messages */
+}ParallelCopyLineBoundary;
+
+/*
+ * Circular queue used to store the line information.
+ */
+typedef struct ParallelCopyLineBoundaries
+{
+ /* Position for the leader to populate a line. */
+ uint32 pos;
+
+ /* Data read from the file/stdin by the leader process. */
+ ParallelCopyLineBoundary ring[RINGSIZE];
+}ParallelCopyLineBoundaries;
+
+/*
+ * Shared information among parallel copy workers. This will be allocated in the
+ * DSM segment.
+ */
+typedef struct ParallelCopyShmInfo
+{
+ bool is_read_in_progress; /* file read status */
+
+ /*
+ * Actual lines inserted by worker, will not be same as
+ * total_worker_processed if where condition is specified along with copy.
+ * This will be the actual records inserted into the relation.
+ */
+ pg_atomic_uint64 processed;
+
+ /*
+ * The number of records currently processed by the worker, this will also
+ * include the number of records that was filtered because of where clause.
+ */
+ pg_atomic_uint64 total_worker_processed;
+ uint64 populated; /* lines populated by leader */
+ uint32 cur_block_pos; /* current data block */
+ ParallelCopyDataBlock data_blocks[MAX_BLOCKS_COUNT]; /* data block array */
+ FullTransactionId full_transaction_id; /* xid for copy from statement */
+ CommandId mycid; /* command id */
+ ParallelCopyLineBoundaries line_boundaries; /* line array */
+} ParallelCopyShmInfo;
+
+/*
+ * Parallel copy line buffer information.
+ */
+typedef struct ParallelCopyLineBuf
+{
+ StringInfoData line_buf;
+ uint64 cur_lineno; /* line number for error messages */
+}ParallelCopyLineBuf;
+
+/*
+ * Parallel copy data information.
+ */
+typedef struct ParallelCopyData
+{
+ Oid relid; /* relation id of the table */
+ ParallelCopyShmInfo *pcshared_info; /* common info in shared memory */
+ bool is_leader;
+
+ /* line position which worker is processing */
+ uint32 worker_processed_pos;
+
+ /*
+ * Local line_buf array, workers will copy it here and release the lines
+ * for the leader to continue.
+ */
+ ParallelCopyLineBuf worker_line_buf[WORKER_CHUNK_COUNT];
+ uint32 worker_line_buf_count; /* Number of lines */
+
+ /* Current position in worker_line_buf */
+ uint32 worker_line_buf_pos;
+}ParallelCopyData;
+
+/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
* even though some fields are used in only some cases.
@@ -230,10 +401,38 @@ typedef struct CopyStateData
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
+ int nworkers;
+ bool is_parallel;
+ ParallelCopyData *pcdata;
/* Shorthand for number of unconsumed bytes available in raw_buf */
#define RAW_BUF_BYTES(cstate) ((cstate)->raw_buf_len - (cstate)->raw_buf_index)
} CopyStateData;
+/*
+ * This structure helps in storing the common data from CopyStateData that are
+ * required by the workers. This information will then be allocated and stored
+ * into the DSM for the worker to retrieve and copy it to CopyStateData.
+ */
+typedef struct SerializedParallelCopyState
+{
+ /* low-level state data */
+ CopyDest copy_dest; /* type of copy source/destination */
+ int file_encoding; /* file or remote side's character encoding */
+ bool need_transcoding; /* file encoding diff from server? */
+ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */
+
+ /* parameters from the COPY command */
+ bool csv_mode; /* Comma Separated Value format? */
+ bool header_line; /* CSV header line? */
+ int null_print_len; /* length of same */
+ bool force_quote_all; /* FORCE_QUOTE *? */
+ bool convert_selectively; /* do selective binary conversion? */
+
+ /* Working state for COPY FROM */
+ AttrNumber num_defaults;
+ Oid relid;
+}SerializedParallelCopyState;
+
/* DestReceiver for COPY (query) TO */
typedef struct
{
@@ -263,6 +462,22 @@ typedef struct
/* Trim the list of buffers back down to this number after flushing */
#define MAX_PARTITION_BUFFERS 32
+/* DSM keys for parallel copy. */
+#define PARALLEL_COPY_KEY_SHARED_INFO 1
+#define PARALLEL_COPY_KEY_CSTATE 2
+#define PARALLEL_COPY_KEY_NULL_PRINT 3
+#define PARALLEL_COPY_KEY_DELIM 4
+#define PARALLEL_COPY_KEY_QUOTE 5
+#define PARALLEL_COPY_KEY_ESCAPE 6
+#define PARALLEL_COPY_KEY_ATTNAME_LIST 7
+#define PARALLEL_COPY_KEY_NOT_NULL_LIST 8
+#define PARALLEL_COPY_KEY_NULL_LIST 9
+#define PARALLEL_COPY_KEY_CONVERT_LIST 10
+#define PARALLEL_COPY_KEY_WHERE_CLAUSE_STR 11
+#define PARALLEL_COPY_KEY_RANGE_TABLE 12
+#define PARALLEL_COPY_WAL_USAGE 13
+#define PARALLEL_COPY_BUFFER_USAGE 14
+
/* Stores multi-insert data related to a single relation in CopyFrom. */
typedef struct CopyMultiInsertBuffer
{
@@ -424,11 +639,477 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
+static pg_attribute_always_inline void EndParallelCopy(ParallelContext *pcxt);
static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
- List *attnamelist);
+ List *attnamelist);
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
+
+/*
+ * SerializeParallelCopyState - Copy shared_cstate using cstate information.
+ */
+static pg_attribute_always_inline void
+SerializeParallelCopyState(CopyState cstate, SerializedParallelCopyState *shared_cstate)
+{
+ shared_cstate->copy_dest = cstate->copy_dest;
+ shared_cstate->file_encoding = cstate->file_encoding;
+ shared_cstate->need_transcoding = cstate->need_transcoding;
+ shared_cstate->encoding_embeds_ascii = cstate->encoding_embeds_ascii;
+ shared_cstate->csv_mode = cstate->csv_mode;
+ shared_cstate->header_line = cstate->header_line;
+ shared_cstate->null_print_len = cstate->null_print_len;
+ shared_cstate->force_quote_all = cstate->force_quote_all;
+ shared_cstate->convert_selectively = cstate->convert_selectively;
+ shared_cstate->num_defaults = cstate->num_defaults;
+ shared_cstate->relid = cstate->pcdata->relid;
+}
+
+/*
+ * RestoreString - Retrieve the string from shared memory.
+ */
+static void
+RestoreString(shm_toc *toc, int sharedkey, char **copystr)
+{
+ char *shared_str_val = (char *)shm_toc_lookup(toc, sharedkey, true);
+ if (shared_str_val)
+ *copystr = pstrdup(shared_str_val);
+}
+
+/*
+ * EstimateLineKeysStr - Estimate the size required in shared memory for the
+ * input string.
+ */
+static void
+EstimateLineKeysStr(ParallelContext *pcxt, char *inputstr)
+{
+ if (inputstr)
+ {
+ shm_toc_estimate_chunk(&pcxt->estimator, strlen(inputstr) + 1);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ }
+}
+
+/*
+ * SerializeString - Insert a string into shared memory.
+ */
+static void
+SerializeString(ParallelContext *pcxt, int key, char *inputstr)
+{
+ if (inputstr)
+ {
+ char *shmptr = (char *)shm_toc_allocate(pcxt->toc,
+ strlen(inputstr) + 1);
+ strcpy(shmptr, inputstr);
+ shm_toc_insert(pcxt->toc, key, shmptr);
+ }
+}
+
+/*
+ * PopulateParallelCopyShmInfo - set ParallelCopyShmInfo.
+ */
+static void
+PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
+ FullTransactionId full_transaction_id)
+{
+ uint32 count;
+
+ MemSet(shared_info_ptr, 0, sizeof(ParallelCopyShmInfo));
+ shared_info_ptr->is_read_in_progress = true;
+ shared_info_ptr->cur_block_pos = -1;
+ shared_info_ptr->full_transaction_id = full_transaction_id;
+ shared_info_ptr->mycid = GetCurrentCommandId(true);
+ for (count = 0; count < RINGSIZE; count++)
+ {
+ ParallelCopyLineBoundary *lineInfo = &shared_info_ptr->line_boundaries.ring[count];
+ pg_atomic_init_u32(&(lineInfo->line_size), -1);
+ }
+}
+
+/*
+ * BeginParallelCopy - start parallel copy tasks.
+ *
+ * Get the number of workers required to perform the parallel copy. The data
+ * structures that are required by the parallel workers will be initialized, the
+ * size required in DSM will be calculated and the necessary keys will be loaded
+ * in the DSM. The specified number of workers will then be launched.
+ */
+static ParallelContext*
+BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
+{
+ ParallelContext *pcxt;
+ ParallelCopyShmInfo *shared_info_ptr;
+ SerializedParallelCopyState *shared_cstate;
+ FullTransactionId full_transaction_id;
+ Size est_cstateshared;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ int parallel_workers = 0;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+ ParallelCopyData *pcdata;
+ MemoryContext oldcontext;
+
+ parallel_workers = Min(nworkers, max_worker_processes);
+
+ /* Can't perform copy in parallel */
+ if (parallel_workers <= 0)
+ return NULL;
+
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ MemoryContextSwitchTo(oldcontext);
+ cstate->pcdata = pcdata;
+
+ EnterParallelMode();
+ pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
+ parallel_workers);
+ Assert(pcxt->nworkers > 0);
+
+ /*
+ * Estimate size for shared information for PARALLEL_COPY_KEY_SHARED_INFO
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(ParallelCopyShmInfo));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_CSTATE */
+ est_cstateshared = MAXALIGN(sizeof(SerializedParallelCopyState));
+ shm_toc_estimate_chunk(&pcxt->estimator, est_cstateshared);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ EstimateLineKeysStr(pcxt, cstate->null_print);
+ EstimateLineKeysStr(pcxt, cstate->null_print_client);
+ EstimateLineKeysStr(pcxt, cstate->delim);
+ EstimateLineKeysStr(pcxt, cstate->quote);
+ EstimateLineKeysStr(pcxt, cstate->escape);
+
+ if (cstate->whereClause != NULL)
+ {
+ whereClauseStr = nodeToString(cstate->whereClause);
+ EstimateLineKeysStr(pcxt, whereClauseStr);
+ }
+
+ if (cstate->range_table != NULL)
+ {
+ rangeTableStr = nodeToString(cstate->range_table);
+ EstimateLineKeysStr(pcxt, rangeTableStr);
+ }
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_XID. */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(FullTransactionId));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_ATTNAME_LIST.
+ */
+ if (attnamelist != NIL)
+ {
+ attnameListStr = nodeToString(attnamelist);
+ EstimateLineKeysStr(pcxt, attnameListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NOT_NULL_LIST.
+ */
+ if (cstate->force_notnull != NIL)
+ {
+ notnullListStr = nodeToString(cstate->force_notnull);
+ EstimateLineKeysStr(pcxt, notnullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NULL_LIST.
+ */
+ if (cstate->force_null != NIL)
+ {
+ nullListStr = nodeToString(cstate->force_null);
+ EstimateLineKeysStr(pcxt, nullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_CONVERT_LIST.
+ */
+ if (cstate->convert_select != NIL)
+ {
+ convertListStr = nodeToString(cstate->convert_select);
+ EstimateLineKeysStr(pcxt, convertListStr);
+ }
+
+ /*
+ * Estimate space for WalUsage and BufferUsage -- PARALLEL_COPY_WAL_USAGE
+ * and PARALLEL_COPY_BUFFER_USAGE.
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ InitializeParallelDSM(pcxt);
+
+ /* If no DSM segment was available, back out (do serial copy) */
+ if (pcxt->seg == NULL)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /* Allocate shared memory for PARALLEL_COPY_KEY_SHARED_INFO */
+ shared_info_ptr = (ParallelCopyShmInfo *) shm_toc_allocate(pcxt->toc, sizeof(ParallelCopyShmInfo));
+ PopulateParallelCopyShmInfo(shared_info_ptr, full_transaction_id);
+
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_SHARED_INFO, shared_info_ptr);
+ pcdata->pcshared_info = shared_info_ptr;
+ pcdata->relid = relid;
+
+ /* Store shared build state, for which we reserved space. */
+ shared_cstate = (SerializedParallelCopyState *)shm_toc_allocate(pcxt->toc, est_cstateshared);
+
+ /* copy cstate variables. */
+ SerializeParallelCopyState(cstate, shared_cstate);
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_CSTATE, shared_cstate);
+
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ATTNAME_LIST, attnameListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NOT_NULL_LIST, notnullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_LIST, nullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_CONVERT_LIST, convertListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, whereClauseStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_RANGE_TABLE, rangeTableStr);
+
+ /*
+ * Allocate space for each worker's WalUsage and BufferUsage; no need to
+ * initialize.
+ */
+ walusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_WAL_USAGE, walusage);
+ bufferusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_BUFFER_USAGE, bufferusage);
+
+ LaunchParallelWorkers(pcxt);
+ if (pcxt->nworkers_launched == 0)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /*
+ * Caller needs to wait for all launched workers when we return. Make sure
+ * that the failure-to-start case will not hang forever.
+ */
+ WaitForParallelWorkersToAttach(pcxt);
+
+ pcdata->is_leader = true;
+ cstate->is_parallel = true;
+ return pcxt;
+}
+
+/*
+ * EndParallelCopy - end the parallel copy tasks.
+ */
+static pg_attribute_always_inline void
+EndParallelCopy(ParallelContext *pcxt)
+{
+ Assert(!IsParallelWorker());
+
+ DestroyParallelContext(pcxt);
+ ExitParallelMode();
+}
+
+/*
+ * InitializeParallelCopyInfo - Initialize parallel worker.
+ */
+static void
+InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
+ CopyState cstate, List *attnamelist)
+{
+ uint32 count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ cstate->copy_dest = shared_cstate->copy_dest;
+ cstate->file_encoding = shared_cstate->file_encoding;
+ cstate->need_transcoding = shared_cstate->need_transcoding;
+ cstate->encoding_embeds_ascii = shared_cstate->encoding_embeds_ascii;
+ cstate->csv_mode = shared_cstate->csv_mode;
+ cstate->header_line = shared_cstate->header_line;
+ cstate->null_print_len = shared_cstate->null_print_len;
+ cstate->force_quote_all = shared_cstate->force_quote_all;
+ cstate->convert_selectively = shared_cstate->convert_selectively;
+ cstate->num_defaults = shared_cstate->num_defaults;
+ pcdata->relid = shared_cstate->relid;
+
+ PopulateCommonCstateInfo(cstate, tup_desc, attnamelist);
+
+ /* Initialize state variables. */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /* Set up variables to avoid per-attribute overhead. */
+ initStringInfo(&cstate->attribute_buf);
+
+ initStringInfo(&cstate->line_buf);
+ for (count = 0; count < WORKER_CHUNK_COUNT; count++)
+ initStringInfo(&pcdata->worker_line_buf[count].line_buf);
+
+ cstate->line_buf_converted = false;
+ cstate->raw_buf = NULL;
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+}
+
+/*
+ * ParallelCopyMain - parallel copy worker's code.
+ *
+ * Where clause handling, convert tuple to columns, add default null values for
+ * the missing columns that are not present in that record. Find the partition
+ * if it is partitioned table, invoke before row insert Triggers, handle
+ * constraints and insert the tuples.
+ */
+void
+ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
+{
+ CopyState cstate;
+ ParallelCopyData *pcdata;
+ ParallelCopyShmInfo *pcshared_info;
+ SerializedParallelCopyState *shared_cstate;
+ Relation rel = NULL;
+ MemoryContext oldcontext;
+ List *attlist = NIL;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+
+ /* Allocate workspace and zero all fields. */
+ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData));
+
+ /*
+ * We allocate everything used by a cstate in a new memory context. This
+ * avoids memory leaks during repeated use of COPY in a query.
+ */
+ cstate->copycontext = AllocSetContextCreate(CurrentMemoryContext,
+ "COPY",
+ ALLOCSET_DEFAULT_SIZES);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ cstate->pcdata = pcdata;
+ pcdata->is_leader = false;
+ pcdata->worker_processed_pos = -1;
+ cstate->is_parallel = true;
+ pcshared_info = (ParallelCopyShmInfo *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_SHARED_INFO, false);
+
+ ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
+
+ pcdata->pcshared_info = pcshared_info;
+
+ shared_cstate = (SerializedParallelCopyState *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
+ cstate->null_print = (char *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_DELIM, &cstate->delim);
+ RestoreString(toc, PARALLEL_COPY_KEY_QUOTE, &cstate->quote);
+ RestoreString(toc, PARALLEL_COPY_KEY_ESCAPE, &cstate->escape);
+ RestoreString(toc, PARALLEL_COPY_KEY_ATTNAME_LIST, &attnameListStr);
+ if (attnameListStr)
+ attlist = (List *)stringToNode(attnameListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NOT_NULL_LIST, ¬nullListStr);
+ if (notnullListStr)
+ cstate->force_notnull = (List *)stringToNode(notnullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NULL_LIST, &nullListStr);
+ if (nullListStr)
+ cstate->force_null = (List *)stringToNode(nullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_CONVERT_LIST, &convertListStr);
+ if (convertListStr)
+ cstate->convert_select = (List *)stringToNode(convertListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, &whereClauseStr);
+ RestoreString(toc, PARALLEL_COPY_KEY_RANGE_TABLE, &rangeTableStr);
+
+ if (whereClauseStr)
+ {
+ Node *whereClauseCnvrtdFrmStr = (Node *) stringToNode(whereClauseStr);
+ cstate->whereClause = whereClauseCnvrtdFrmStr;
+ }
+
+ if (rangeTableStr)
+ {
+ List *rangeTableCnvrtdFrmStr = (List *) stringToNode(rangeTableStr);
+ cstate->range_table = rangeTableCnvrtdFrmStr;
+ }
+
+ /* Open and lock the relation, using the appropriate lock type. */
+ rel = table_open(shared_cstate->relid, RowExclusiveLock);
+ cstate->rel = rel;
+ InitializeParallelCopyInfo(shared_cstate, cstate, attlist);
+
+ CopyFrom(cstate);
+
+ if (rel != NULL)
+ table_close(rel, RowExclusiveLock);
+
+ /* Report WAL/buffer usage during parallel execution */
+ bufferusage = shm_toc_lookup(toc, PARALLEL_COPY_BUFFER_USAGE, false);
+ walusage = shm_toc_lookup(toc, PARALLEL_COPY_WAL_USAGE, false);
+ InstrEndParallelQuery(&bufferusage[ParallelWorkerNumber],
+ &walusage[ParallelWorkerNumber]);
+
+ MemoryContextSwitchTo(oldcontext);
+ pfree(cstate);
+ return;
+}
+
+/*
+ * ParallelCopyFrom - parallel copy leader's functionality.
+ *
+ * Leader executes the before statement for before statement trigger, if before
+ * statement trigger is present. It will read the table data from the file and
+ * copy the contents to DSM data blocks. It will then read the input contents
+ * from the DSM data block and identify the records based on line breaks. This
+ * information is called line or a record that need to be inserted into a
+ * relation. The line information will be stored in ParallelCopyLineBoundary DSM
+ * data structure. Workers will then process this information and insert the
+ * data in to table. It will repeat this process until the all data is read from
+ * the file and all the DSM data blocks are processed. While processing if
+ * leader identifies that DSM Data blocks or DSM ParallelCopyLineBoundary data
+ * structures is full, leader will wait till the worker frees up some entries
+ * and repeat the process. It will wait till all the lines populated are
+ * processed by the workers and exits.
+ */
+static void
+ParallelCopyFrom(CopyState cstate)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+
+ pcshared_info->is_read_in_progress = false;
+ cstate->cur_lineno = 0;
+}
+
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -1141,6 +1822,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
if (is_from)
{
+ ParallelContext *pcxt = NULL;
Assert(rel);
/* check read-only transaction and parallel mode */
@@ -1150,7 +1832,24 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);
cstate->whereClause = whereClause;
- *processed = CopyFrom(cstate); /* copy from file to database */
+ cstate->is_parallel = false;
+
+ if (cstate->nworkers > 0)
+ pcxt = BeginParallelCopy(cstate->nworkers, cstate, stmt->attlist,
+ relid);
+
+ if (pcxt)
+ {
+ ParallelCopyFrom(cstate);
+
+ /* Wait for all copy workers to finish */
+ WaitForParallelWorkersToFinish(pcxt);
+ *processed = pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+ EndParallelCopy(pcxt);
+ }
+ else
+ *processed = CopyFrom(cstate); /* copy from file to database */
+
EndCopyFrom(cstate);
}
else
@@ -1199,6 +1898,7 @@ ProcessCopyOptions(ParseState *pstate,
cstate->is_copy_from = is_from;
cstate->file_encoding = -1;
+ cstate->nworkers = -1;
/* Extract options from the statement node tree */
foreach(option, options)
@@ -1367,6 +2067,53 @@ ProcessCopyOptions(ParseState *pstate,
defel->defname),
parser_errposition(pstate, defel->location)));
}
+ else if (strcmp(defel->defname, "parallel") == 0)
+ {
+ char *endptr, *str;
+ long val;
+
+ if (!cstate->is_copy_from)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("parallel option supported only for copy from"),
+ parser_errposition(pstate, defel->location)));
+ if (cstate->nworkers >= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options"),
+ parser_errposition(pstate, defel->location)));
+
+ str = defGetString(defel);
+
+ errno = 0; /* To distinguish success/failure after call */
+ val = strtol(str, &endptr, 10);
+
+ /* Check for various possible errors */
+ if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+ || (errno != 0 && val == 0) ||
+ *endptr)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("improper use of argument to option \"%s\"",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
+
+ if (endptr == str)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("no digits were found in argument to option \"%s\"",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
+
+ cstate->nworkers = (int) val;
+
+ if (cstate->nworkers <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("argument to option \"%s\" must be a positive integer greater than zero",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
+ }
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1720,11 +2467,12 @@ BeginCopy(ParseState *pstate,
/*
* PopulateCommonCstateInfo - Populates the common variables required for copy
- * from operation. This is a helper function for BeginCopy function.
+ * from operation. This is a helper function for BeginCopy &
+ * InitializeParallelCopyInfo function.
*/
static void
PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc,
- List *attnamelist)
+ List *attnamelist)
{
int num_phys_attrs;
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index c639833..82843c6 100644
--- a/src/include/commands/copy.h
+++ b/src/include/commands/copy.h
@@ -14,6 +14,7 @@
#ifndef COPY_H
#define COPY_H
+#include "access/parallel.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
#include "parser/parse_node.h"
@@ -41,4 +42,5 @@ extern uint64 CopyFrom(CopyState cstate);
extern DestReceiver *CreateCopyDestReceiver(void);
+extern void ParallelCopyMain(dsm_segment *seg, shm_toc *toc);
#endif /* COPY_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 7eaaad1..3a83d0f 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1699,6 +1699,12 @@ ParallelBitmapHeapState
ParallelBlockTableScanDesc
ParallelCompletionPtr
ParallelContext
+ParallelCopyLineBoundaries
+ParallelCopyLineBoundary
+ParallelCopyData
+ParallelCopyDataBlock
+ParallelCopyLineBuf
+ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
ParallelHashJoinBatch
@@ -2215,6 +2221,7 @@ SerCommitSeqNo
SerialControl
SerializableXactHandle
SerializedActiveRelMaps
+SerializedParallelCopyState
SerializedReindexState
SerializedSnapshotData
SerializedTransactionState
--
1.8.3.1
[text/x-patch] v2-0003-Allow-copy-from-command-to-process-data-from-file.patch (43.0K, ../../CALDaNm2EYd67r3NwScaFh9_onbX_vpKJVS9p-=+TX22q47m+Zg@mail.gmail.com/5-v2-0003-Allow-copy-from-command-to-process-data-from-file.patch)
download | inline diff:
From f3ec188c3c1e11fdbb716ec12862a891a95b396f Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:31:42 +0530
Subject: [PATCH v2 3/5] Allow copy from command to process data from
file/STDIN contents to a table in parallel.
This feature allows the copy from to leverage multiple CPUs in order to copy
data from file/STDIN to a table. This adds a PARALLEL option to COPY FROM
command where the user can specify the number of workers that can be used
to perform the COPY FROM command. Specifying zero as number of workers will
disable parallelism.
The backend, to which the "COPY FROM" query is submitted acts as leader with
the responsibility of reading data from the file/stdin, launching at most n
number of workers as specified with PARALLEL 'n' option in the "COPY FROM"
query. The leader populates the common data required for the workers execution
in the DSM and shares it with the workers. The leader then executes before
statement triggers if there exists any. Leader populates DSM lines which
includes the start offset and line size, while populating the lines it reads
as many blocks as required into the DSM data blocks from the file. Each block
is of 64K size. The leader parses the data to identify a line, the existing
logic from CopyReadLineText which identifies the lines with some changes was
used for this. Leader checks if a free line is available to copy the
information, if there is no free line it waits till the required line is
freed up by the worker and then copies the identified lines information
(offset & line size) into the DSM lines. This process is repeated till the
complete file is processed. Simultaneously, the workers cache the lines(50)
locally into the local memory and release the lines to the leader for further
populating. Each worker processes the lines that it cached and inserts it into
the table. The leader does not participate in the insertion of data, leaders
only responsibility will be to identify the lines as fast as possible for the
workers to do the actual copy operation. The leader waits till all the lines
populated are processed by the workers and exits.
We have chosen this design based on the reason "that everything stalls if the
leader doesn't accept further input data, as well as when there are no
available splitted chunks so it doesn't seem like a good idea to have the
leader do other work. This is backed by the performance data where we have
seen that with 1 worker there is just a 5-10% performance difference".
---
src/backend/access/common/toast_internals.c | 11 +-
src/backend/access/heap/heapam.c | 13 -
src/backend/access/transam/xact.c | 31 +
src/backend/commands/copy.c | 890 ++++++++++++++++++++++++++--
src/bin/psql/tab-complete.c | 2 +-
src/include/access/xact.h | 2 +
src/tools/pgindent/typedefs.list | 1 +
7 files changed, 896 insertions(+), 54 deletions(-)
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 25a81e5..586d53d 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -17,6 +17,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heaptoast.h"
+#include "access/parallel.h"
#include "access/table.h"
#include "access/toast_internals.h"
#include "access/xact.h"
@@ -116,7 +117,15 @@ toast_save_datum(Relation rel, Datum value,
TupleDesc toasttupDesc;
Datum t_values[3];
bool t_isnull[3];
- CommandId mycid = GetCurrentCommandId(true);
+
+ /*
+ * Parallel copy can insert toast tuples, in case of parallel copy the
+ * command would have been set already by calling AssignCommandIdForWorker.
+ * For parallel copy call GetCurrentCommandId to get currentCommandId by
+ * passing used as false, as this is taken care earlier.
+ */
+ CommandId mycid = IsParallelWorker() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
struct varlena *result;
struct varatt_external toast_pointer;
union
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 5eef225..bd7a7fc 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2023,19 +2023,6 @@ static HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
- /*
- * Parallel operations are required to be strictly read-only in a parallel
- * worker. Parallel inserts are not safe even in the leader in the
- * general case, because group locking means that heavyweight locks for
- * relation extension or GIN page locks will not conflict between members
- * of a lock group, but we don't prohibit that case here because there are
- * useful special cases that we can safely allow, such as CREATE TABLE AS.
- */
- if (IsParallelWorker())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot insert tuples in a parallel worker")));
-
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index d4f7c29..9bff390 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -504,6 +504,37 @@ GetCurrentFullTransactionIdIfAny(void)
}
/*
+ * AssignFullTransactionIdForWorker
+ *
+ * For parallel copy, transaction id of leader will be used by the workers.
+ */
+void
+AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId)
+{
+ TransactionState s = CurrentTransactionState;
+
+ Assert((IsInParallelMode() || IsParallelWorker()));
+ s->fullTransactionId = fullTransactionId;
+}
+
+/*
+ * AssignCommandIdForWorker
+ *
+ * For parallel copy, command id of leader will be used by the workers.
+ */
+void
+AssignCommandIdForWorker(CommandId commandId, bool used)
+{
+ Assert((IsInParallelMode() || IsParallelWorker()));
+
+ /* this is global to a transaction, not subtransaction-local */
+ if (used)
+ currentCommandIdUsed = true;
+
+ currentCommandId = commandId;
+}
+
+/*
* MarkCurrentTransactionIdLoggedIfAny
*
* Remember that the current xid - if it is assigned - now has been wal logged.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3dcdfc1..c65fc98 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -26,6 +26,7 @@
#include "access/xlog.h"
#include "catalog/dependency.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_proc_d.h"
#include "catalog/pg_type.h"
#include "commands/copy.h"
#include "commands/defrem.h"
@@ -40,11 +41,13 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
+#include "pgstat.h"
#include "port/pg_bswap.h"
#include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
@@ -95,6 +98,18 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+/*
+ * State of the line.
+ */
+typedef enum ParallelCopyLineState
+{
+ LINE_INIT, /* initial state of line */
+ LINE_LEADER_POPULATING, /* leader processing line */
+ LINE_LEADER_POPULATED, /* leader completed populating line */
+ LINE_WORKER_PROCESSING, /* worker processing line */
+ LINE_WORKER_PROCESSED /* worker completed processing line */
+}ParallelCopyLineState;
+
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
/*
@@ -123,6 +138,7 @@ typedef enum CopyInsertMethod
#define IsParallelCopy() (cstate->is_parallel)
#define IsLeader() (cstate->pcdata->is_leader)
+#define IsWorker() (IsParallelCopy() && !IsLeader())
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
@@ -556,9 +572,13 @@ if (1) \
{ \
if (raw_buf_ptr > cstate->raw_buf_index) \
{ \
- appendBinaryStringInfo(&cstate->line_buf, \
- cstate->raw_buf + cstate->raw_buf_index, \
- raw_buf_ptr - cstate->raw_buf_index); \
+ if (!IsParallelCopy()) \
+ appendBinaryStringInfo(&cstate->line_buf, \
+ cstate->raw_buf + cstate->raw_buf_index, \
+ raw_buf_ptr - cstate->raw_buf_index); \
+ else \
+ line_size += raw_buf_ptr - cstate->raw_buf_index; \
+ \
cstate->raw_buf_index = raw_buf_ptr; \
} \
} else ((void) 0)
@@ -571,26 +591,65 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/* Begin parallel copy Macros */
+#define SET_NEWLINE_SIZE() \
+{ \
+ if (cstate->eol_type == EOL_NL || cstate->eol_type == EOL_CR) \
+ new_line_size = 1; \
+ else if (cstate->eol_type == EOL_CRNL) \
+ new_line_size = 2; \
+ else \
+ new_line_size = 0; \
+}
+
+/*
+ * COPY_WAIT_TO_PROCESS - Wait before continuing to process.
+ */
+#define COPY_WAIT_TO_PROCESS() \
+{ \
+ CHECK_FOR_INTERRUPTS(); \
+ (void) WaitLatch(MyLatch, \
+ WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, \
+ 1L, WAIT_EVENT_PG_SLEEP); \
+ ResetLatch(MyLatch); \
+}
+
/*
* CLEAR_EOL_LINE - Wrapper for clearing EOL.
*/
#define CLEAR_EOL_LINE() \
if (!result && !IsHeaderLine()) \
- ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
- cstate->line_buf.len, \
- &cstate->line_buf.len) \
+{ \
+ if (IsParallelCopy()) \
+ ClearEOLFromCopiedData(cstate, cstate->raw_buf, \
+ raw_buf_ptr, &line_size); \
+ else \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len); \
+} \
/*
* INCREMENTPROCESSED - Increment the lines processed.
*/
-#define INCREMENTPROCESSED(processed) \
-processed++;
+#define INCREMENTPROCESSED(processed) \
+{ \
+ if (!IsParallelCopy()) \
+ processed++; \
+ else \
+ pg_atomic_add_fetch_u64(&cstate->pcdata->pcshared_info->processed, 1); \
+}
/*
* GETPROCESSED - Get the lines processed.
*/
#define GETPROCESSED(processed) \
-return processed;
+if (!IsParallelCopy()) \
+ return processed; \
+else \
+ return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+
+/* End parallel copy Macros */
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -645,7 +704,10 @@ static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
-
+static void ExecBeforeStmtTrigger(CopyState cstate);
+static void CheckTargetRelValidity(CopyState cstate);
+static void PopulateCstateCatalogInfo(CopyState cstate);
+static pg_attribute_always_inline uint32 GetLinePosition(CopyState cstate);
/*
* SerializeParallelCopyState - Copy shared_cstate using cstate information.
*/
@@ -727,6 +789,137 @@ PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
}
/*
+ * IsTriggerFunctionParallelSafe - Check if the trigger function is parallel
+ * safe for the triggers. Return false if any one of the trigger has parallel
+ * unsafe function.
+ */
+static pg_attribute_always_inline bool
+IsTriggerFunctionParallelSafe(TriggerDesc *trigdesc)
+{
+ int i;
+ for (i = 0; i < trigdesc->numtriggers; i++)
+ {
+ Trigger *trigger = &trigdesc->triggers[i];
+ int trigtype = RI_TRIGGER_NONE;
+
+ if (func_parallel(trigger->tgfoid) != PROPARALLEL_SAFE)
+ return false;
+
+ /* If the trigger is parallel safe, also look for RI_TRIGGER. */
+ trigtype = RI_FKey_trigger_type(trigger->tgfoid);
+ if (trigtype == RI_TRIGGER_PK || trigtype == RI_TRIGGER_FK)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * CheckExprParallelSafety - determine parallel safety of volatile expressions
+ * in default clause of column definition or in where clause and return true if
+ * they are parallel safe.
+ */
+static pg_attribute_always_inline bool
+CheckExprParallelSafety(CopyState cstate)
+{
+ if (contain_volatile_functions(cstate->whereClause))
+ {
+ if (max_parallel_hazard((Query *)cstate->whereClause) != PROPARALLEL_SAFE)
+ return false;
+ }
+
+ /*
+ * Check if any of the column has volatile default expression. if yes, and
+ * they are not parallel safe, then parallelism is not allowed. For
+ * instance, if there are any serial/bigserial columns for which nextval()
+ * default expression which is parallel unsafe is associated, parallelism
+ * should not be allowed. In non parallel copy volatile functions are not
+ * checked for nextval().
+ */
+ if (cstate->defexprs != NULL && cstate->num_defaults != 0)
+ {
+ int i;
+ for (i = 0; i < cstate->num_defaults; i++)
+ {
+ bool volatile_expr = contain_volatile_functions((Node *)cstate->defexprs[i]->expr);
+ if (volatile_expr &&
+ (max_parallel_hazard((Query *)cstate->defexprs[i]->expr)) !=
+ PROPARALLEL_SAFE)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * FindInsertMethod - determine insert mode single, multi, or multi conditional.
+ */
+static pg_attribute_always_inline CopyInsertMethod
+FindInsertMethod(CopyState cstate)
+{
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_before_row ||
+ cstate->rel->trigdesc->trig_insert_instead_row))
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+ cstate->rel->trigdesc != NULL &&
+ cstate->rel->trigdesc->trig_insert_new_table)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ return CIM_MULTI_CONDITIONAL;
+
+ return CIM_MULTI;
+}
+
+/*
+ * IsParallelCopyAllowed - check for the cases where parallel copy is not
+ * applicable.
+ */
+static pg_attribute_always_inline bool
+IsParallelCopyAllowed(CopyState cstate)
+{
+ /* Parallel copy not allowed for frontend (2.0 protocol) & binary option. */
+ if ((cstate->copy_dest == COPY_OLD_FE) || cstate->binary)
+ return false;
+
+ /* Check if copy is into foreign table or temporary table. */
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
+ RelationUsesLocalBuffers(cstate->rel))
+ return false;
+
+ /* Check if trigger function is parallel safe. */
+ if (cstate->rel->trigdesc != NULL &&
+ !IsTriggerFunctionParallelSafe(cstate->rel->trigdesc))
+ return false;
+
+ /*
+ * Check if there is after statement or instead of trigger or transition
+ * table triggers.
+ */
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_after_statement ||
+ cstate->rel->trigdesc->trig_insert_instead_row ||
+ cstate->rel->trigdesc->trig_insert_new_table))
+ return false;
+
+ /* Check if the volatile expressions are parallel safe, if present any. */
+ if (!CheckExprParallelSafety(cstate))
+ return false;
+
+ /* Check if the insertion mode is single. */
+ if (FindInsertMethod(cstate) == CIM_SINGLE)
+ return false;
+
+ return true;
+}
+
+/*
* BeginParallelCopy - start parallel copy tasks.
*
* Get the number of workers required to perform the parallel copy. The data
@@ -754,6 +947,7 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
ParallelCopyData *pcdata;
MemoryContext oldcontext;
+ CheckTargetRelValidity(cstate);
parallel_workers = Min(nworkers, max_worker_processes);
/* Can't perform copy in parallel */
@@ -765,6 +959,15 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
MemoryContextSwitchTo(oldcontext);
cstate->pcdata = pcdata;
+ /*
+ * User chosen parallel copy. Determine if the parallel copy is actually
+ * allowed. If not, go with the non-parallel mode.
+ */
+ if (!IsParallelCopyAllowed(cstate))
+ return NULL;
+
+ full_transaction_id = GetCurrentFullTransactionId();
+
EnterParallelMode();
pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
parallel_workers);
@@ -973,9 +1176,212 @@ InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
cstate->line_buf_converted = false;
cstate->raw_buf = NULL;
cstate->raw_buf_index = cstate->raw_buf_len = 0;
+
+ PopulateCstateCatalogInfo(cstate);
+
+ /* Create workspace for CopyReadAttributes results. */
+ if (!cstate->binary)
+ {
+ AttrNumber attr_count = list_length(cstate->attnumlist);
+
+ cstate->max_fields = attr_count;
+ cstate->raw_fields = (char **)palloc(attr_count * sizeof(char *));
+ }
+}
+
+/*
+ * CacheLineInfo - Cache the line information to local memory.
+ */
+static bool
+CacheLineInfo(CopyState cstate, uint32 buff_count)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ uint32 write_pos;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 offset;
+ int dataSize;
+ int copiedSize = 0;
+
+ resetStringInfo(&pcdata->worker_line_buf[buff_count].line_buf);
+ write_pos = GetLinePosition(cstate);
+ if (-1 == write_pos)
+ return true;
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ if (pg_atomic_read_u32(&lineInfo->line_size) == 0)
+ goto empty_data_line_update;
+
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ /* Get the offset information from where the data must be copied. */
+ offset = lineInfo->start_offset;
+ pcdata->worker_line_buf[buff_count].cur_lineno = lineInfo->cur_lineno;
+
+ elog(DEBUG1, "[Worker] Processing - line position:%d, block:%d, unprocessed lines:%d, offset:%d, line size:%d",
+ write_pos, lineInfo->first_block,
+ pg_atomic_read_u32(&data_blk_ptr->unprocessed_line_parts),
+ offset, pg_atomic_read_u32(&lineInfo->line_size));
+
+ for (;;)
+ {
+ uint8 skip_bytes = data_blk_ptr->skip_bytes;
+ /*
+ * There is a possibility that the above loop has come out because
+ * data_blk_ptr->curr_blk_completed is set, but dataSize read might
+ * be an old value, if data_blk_ptr->curr_blk_completed and the line is
+ * completed, line_size will be set. Read the line_size again to be
+ * sure if it is complete or partial block.
+ */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+ if (dataSize)
+ {
+ int remainingSize = dataSize - copiedSize;
+ if (!remainingSize)
+ break;
+
+ /* Whole line is in current block. */
+ if (remainingSize + offset + skip_bytes < DATA_BLOCK_SIZE)
+ {
+ appendBinaryStringInfo(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ remainingSize);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts,
+ 1);
+ break;
+ }
+ else
+ {
+ /* Line is spread across the blocks. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+ while (copiedSize < dataSize)
+ {
+ uint32 currentBlockCopySize;
+ ParallelCopyDataBlock *currBlkPtr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ skip_bytes = currBlkPtr->skip_bytes;
+
+ /*
+ * If complete data is present in current block use
+ * dataSize - copiedSize, or copy the whole block from
+ * current block.
+ */
+ currentBlockCopySize = Min(dataSize - copiedSize, DATA_BLOCK_SIZE - skip_bytes);
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &currBlkPtr->data[0],
+ currentBlockCopySize);
+ pg_atomic_sub_fetch_u32(&currBlkPtr->unprocessed_line_parts, 1);
+ copiedSize += currentBlockCopySize;
+ data_blk_ptr = currBlkPtr;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ /* Copy this complete block from the current offset. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+
+ /*
+ * Reset the offset. For the first copy, copy from the offset. For
+ * the subsequent copy the complete block.
+ */
+ offset = 0;
+
+ /* Set data_blk_ptr to the following block. */
+ data_blk_ptr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ }
+
+ for (;;)
+ {
+ /* Get the size of this line */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ /*
+ * If the data is present in current block lineInfo.line_size
+ * will be updated. If the data is spread across the blocks either
+ * of lineInfo.line_size or data_blk_ptr->curr_blk_completed can
+ * be updated. lineInfo.line_size will be updated if the complete
+ * read is finished. data_blk_ptr->curr_blk_completed will be
+ * updated if processing of current block is finished and data
+ * processing is not finished.
+ */
+ if (data_blk_ptr->curr_blk_completed || (dataSize != -1))
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+ }
+
+empty_data_line_update:
+ elog(DEBUG1, "[Worker] Completed processing line:%d", write_pos);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_WORKER_PROCESSED);
+ pg_atomic_write_u32(&lineInfo->line_size, -1);
+ pg_atomic_add_fetch_u64(&pcshared_info->total_worker_processed, 1);
+ return false;
}
/*
+ * GetWorkerLine - Returns a line for worker to process.
+ */
+static bool
+GetWorkerLine(CopyState cstate)
+{
+ uint32 buff_count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+
+ /*
+ * Copy the line data to line_buf and release the line position so that the
+ * worker can continue loading data.
+ */
+ if (pcdata->worker_line_buf_pos < pcdata->worker_line_buf_count)
+ goto return_line;
+
+ pcdata->worker_line_buf_pos = 0;
+ pcdata->worker_line_buf_count = 0;
+
+ for (buff_count = 0; buff_count < WORKER_CHUNK_COUNT; buff_count++)
+ {
+ bool result = CacheLineInfo(cstate, buff_count);
+ if (result)
+ break;
+
+ pcdata->worker_line_buf_count++;
+ }
+
+ if (pcdata->worker_line_buf_count)
+ goto return_line;
+ else
+ resetStringInfo(&cstate->line_buf);
+
+ return true;
+
+return_line:
+ cstate->line_buf = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].line_buf;
+ cstate->cur_lineno = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].cur_lineno;
+ cstate->line_buf_valid = true;
+
+ /* Mark that encoding conversion hasn't occurred yet. */
+ cstate->line_buf_converted = false;
+ ConvertToServerEncoding(cstate);
+ pcdata->worker_line_buf_pos++;
+ return false;
+ }
+
+/*
* ParallelCopyMain - parallel copy worker's code.
*
* Where clause handling, convert tuple to columns, add default null values for
@@ -1024,6 +1430,8 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
pcdata->pcshared_info = pcshared_info;
+ AssignFullTransactionIdForWorker(pcshared_info->full_transaction_id);
+ AssignCommandIdForWorker(pcshared_info->mycid, true);
shared_cstate = (SerializedParallelCopyState *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
cstate->null_print = (char *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
@@ -1084,6 +1492,33 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
}
/*
+ * UpdateBlockInLineInfo - Update the line information.
+ */
+static pg_attribute_always_inline int
+UpdateBlockInLineInfo(CopyState cstate, uint32 blk_pos,
+ uint32 offset, uint32 line_size, uint32 line_state)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ ParallelCopyLineBoundary *lineInfo;
+ int line_pos = lineBoundaryPtr->pos;
+
+ /* Update the line information for the worker to pick and process. */
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ while (pg_atomic_read_u32(&lineInfo->line_size) != -1)
+ COPY_WAIT_TO_PROCESS()
+
+ lineInfo->first_block = blk_pos;
+ lineInfo->start_offset = offset;
+ lineInfo->cur_lineno = cstate->cur_lineno;
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, line_state);
+ lineBoundaryPtr->pos = (lineBoundaryPtr->pos + 1) % RINGSIZE;
+
+ return line_pos;
+}
+
+/*
* ParallelCopyFrom - parallel copy leader's functionality.
*
* Leader executes the before statement for before statement trigger, if before
@@ -1106,8 +1541,298 @@ ParallelCopyFrom(CopyState cstate)
ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+ /* Execute the before statement triggers from the leader */
+ ExecBeforeStmtTrigger(cstate);
+
+ /* On input just throw the header line away. */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ {
+ pcshared_info->is_read_in_progress = false;
+ return; /* done */
+ }
+ }
+
+ for (;;)
+ {
+ bool done;
+ cstate->cur_lineno++;
+
+ /* Actually read the line into memory here. */
+ done = CopyReadLine(cstate);
+
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ break;
+ }
+
pcshared_info->is_read_in_progress = false;
cstate->cur_lineno = 0;
+ }
+
+/*
+ * GetLinePosition - return the line position that worker should process.
+ */
+static uint32
+GetLinePosition(CopyState cstate)
+{
+ ParallelCopyData *pcdata = cstate->pcdata;
+ ParallelCopyShmInfo *pcshared_info = pcdata->pcshared_info;
+ uint32 previous_pos = pcdata->worker_processed_pos;
+ uint32 write_pos = (previous_pos == -1) ? 0 : (previous_pos + 1) % RINGSIZE;
+ for (;;)
+ {
+ int dataSize;
+ bool is_read_in_progress = pcshared_info->is_read_in_progress;
+ ParallelCopyLineBoundary *lineInfo;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineState line_state = LINE_LEADER_POPULATED;
+ ParallelCopyLineState curr_line_state;
+ CHECK_FOR_INTERRUPTS();
+
+ /* File read completed & no elements to process. */
+ if (!is_read_in_progress &&
+ (pcshared_info->populated ==
+ pg_atomic_read_u64(&pcshared_info->total_worker_processed)))
+ {
+ write_pos = -1;
+ break;
+ }
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ curr_line_state = pg_atomic_read_u32(&lineInfo->line_state);
+ if ((write_pos % WORKER_CHUNK_COUNT == 0) &&
+ (curr_line_state == LINE_WORKER_PROCESSED ||
+ curr_line_state == LINE_WORKER_PROCESSING))
+ {
+ pcdata->worker_processed_pos = write_pos;
+ write_pos = (write_pos + WORKER_CHUNK_COUNT) % RINGSIZE;
+ continue;
+ }
+
+ /* Get the size of this line. */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ if (dataSize != 0) /* If not an empty line. */
+ {
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ if (!data_blk_ptr->curr_blk_completed && (dataSize == -1))
+ {
+ /* Wait till the current line or block is added. */
+ COPY_WAIT_TO_PROCESS()
+ continue;
+ }
+ }
+
+ /* Make sure that no worker has consumed this element. */
+ if (pg_atomic_compare_exchange_u32(&lineInfo->line_state,
+ &line_state, LINE_WORKER_PROCESSING))
+ break;
+ }
+
+ pcdata->worker_processed_pos = write_pos;
+ return write_pos;
+}
+
+/*
+ * GetFreeCopyBlock - Get a free block for data to be copied.
+ */
+static pg_attribute_always_inline uint32
+GetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ int count = 0;
+ uint32 last_free_block = pcshared_info->cur_block_pos;
+ uint32 block_pos = (last_free_block != -1) ? ((last_free_block + 1) % MAX_BLOCKS_COUNT): 0;
+
+ /*
+ * Get a new block for copying data, don't check current block, current
+ * block will have some unprocessed data.
+ */
+ while (count < (MAX_BLOCKS_COUNT - 1))
+ {
+ ParallelCopyDataBlock *dataBlkPtr = &pcshared_info->data_blocks[block_pos];
+ uint32 unprocessed_line_parts = pg_atomic_read_u32(&dataBlkPtr->unprocessed_line_parts);
+ if (unprocessed_line_parts == 0)
+ {
+ dataBlkPtr->curr_blk_completed = false;
+ dataBlkPtr->skip_bytes = 0;
+ pcshared_info->cur_block_pos = block_pos;
+ return block_pos;
+ }
+
+ block_pos = (block_pos + 1) % MAX_BLOCKS_COUNT;
+ count++;
+ }
+
+ return -1;
+}
+
+/*
+ * WaitGetFreeCopyBlock - If there are no blocks available, wait and get a block
+ * for copying data.
+ */
+static uint32
+WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ uint32 new_free_pos = -1;
+ for (;;)
+ {
+ new_free_pos = GetFreeCopyBlock(pcshared_info);
+ if (new_free_pos != -1) /* We have got one block, break now. */
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+
+ return new_free_pos;
+}
+
+/*
+ * SetRawBufForLoad - Set raw_buf to the shared memory where the file data must
+ * be read.
+ */
+static void
+SetRawBufForLoad(CopyState cstate, uint32 line_size, uint32 copy_buf_len,
+ uint32 raw_buf_ptr, char **copy_raw_buf)
+{
+ ParallelCopyShmInfo *pcshared_info;
+ uint32 cur_block_pos;
+ uint32 next_block_pos;
+ ParallelCopyDataBlock *cur_data_blk_ptr = NULL;
+ ParallelCopyDataBlock *next_data_blk_ptr = NULL;
+
+ if (!IsParallelCopy())
+ return;
+
+ pcshared_info = cstate->pcdata->pcshared_info;
+ cur_block_pos = pcshared_info->cur_block_pos;
+ cur_data_blk_ptr = (cstate->raw_buf) ? &pcshared_info->data_blocks[cur_block_pos] : NULL;
+ next_block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ next_data_blk_ptr = &pcshared_info->data_blocks[next_block_pos];
+
+ /* set raw_buf to the data block in shared memory */
+ cstate->raw_buf = next_data_blk_ptr->data;
+ *copy_raw_buf = cstate->raw_buf;
+ if (cur_data_blk_ptr && line_size)
+ {
+ /*
+ * Mark the previous block as completed, worker can start copying this
+ * data.
+ */
+ cur_data_blk_ptr->following_block = next_block_pos;
+ pg_atomic_add_fetch_u32(&cur_data_blk_ptr->unprocessed_line_parts, 1);
+ cur_data_blk_ptr->skip_bytes = copy_buf_len - raw_buf_ptr;
+ cur_data_blk_ptr->curr_blk_completed = true;
+ }
+}
+
+/*
+ * EndLineParallelCopy - Update the line information in shared memory.
+ */
+static void
+EndLineParallelCopy(CopyState cstate, uint32 line_pos, uint32 line_size,
+ uint32 raw_buf_ptr)
+{
+ uint8 new_line_size;
+ if (!IsParallelCopy())
+ return;
+
+ if (!IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ SET_NEWLINE_SIZE()
+ if (line_size)
+ {
+ ParallelCopyLineBoundary *lineInfo = &lineBoundaryPtr->ring[line_pos];
+ /*
+ * If the new_line_size > raw_buf_ptr, then the new block has only
+ * new line char content. The unprocessed count should not be
+ * increased in this case.
+ */
+ if (raw_buf_ptr > new_line_size)
+ {
+ uint32 cur_block_pos = pcshared_info->cur_block_pos;
+ ParallelCopyDataBlock *curr_data_blk_ptr = &pcshared_info->data_blocks[cur_block_pos];
+ pg_atomic_add_fetch_u32(&curr_data_blk_ptr->unprocessed_line_parts, 1);
+ }
+
+ /* Update line size. */
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_LEADER_POPULATED);
+ elog(DEBUG1, "[Leader] After adding - line position:%d, line_size:%d",
+ line_pos, line_size);
+ pcshared_info->populated++;
+ }
+ else if (new_line_size)
+ {
+ /* This means only new line char, empty record should be inserted.*/
+ ParallelCopyLineBoundary *lineInfo;
+ line_pos = UpdateBlockInLineInfo(cstate, -1, -1, 0,
+ LINE_LEADER_POPULATED);
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ elog(DEBUG1, "[Leader] Added empty line with offset:%d, line position:%d, line size:%d",
+ lineInfo->start_offset, line_pos,
+ pg_atomic_read_u32(&lineInfo->line_size));
+ pcshared_info->populated++;
+ }
+ }
+}
+
+/*
+ * ExecBeforeStmtTrigger - Execute the before statement trigger, this will be
+ * executed for parallel copy by the leader process.
+ */
+static void
+ExecBeforeStmtTrigger(CopyState cstate)
+{
+ EState *estate = CreateExecutorState();
+ ResultRelInfo *resultRelInfo;
+
+ Assert(IsLeader());
+
+ /*
+ * We need a ResultRelInfo so we can use the regular executor's
+ * index-entry-making machinery. (There used to be a huge amount of code
+ * here that basically duplicated execUtils.c ...)
+ */
+ resultRelInfo = makeNode(ResultRelInfo);
+ InitResultRelInfo(resultRelInfo,
+ cstate->rel,
+ 1, /* must match rel's position in range_table */
+ NULL,
+ 0);
+
+ /* Verify the named relation is a valid target for INSERT */
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
+
+ estate->es_result_relations = resultRelInfo;
+ estate->es_num_result_relations = 1;
+ estate->es_result_relation_info = resultRelInfo;
+
+ ExecInitRangeTable(estate, cstate->range_table);
+
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+
+ /* Close any trigger target relations */
+ ExecCleanUpTriggerState(estate);
+
+ FreeExecutorState(estate);
}
/*
@@ -3584,7 +4309,8 @@ CopyFrom(CopyState cstate)
PartitionTupleRouting *proute = NULL;
ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
+ CommandId mycid = IsParallelCopy() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
int ti_options = 0; /* start with default options for insert */
BulkInsertState bistate = NULL;
CopyInsertMethod insertMethod;
@@ -3594,7 +4320,14 @@ CopyFrom(CopyState cstate)
bool has_instead_insert_row_trig;
bool leafpart_use_multi_insert = false;
- CheckTargetRelValidity(cstate);
+ /*
+ * Perform this check if it is not parallel copy. In case of parallel
+ * copy, this check is done by the leader, so that if any invalid case
+ * exist the copy from command will error out from the leader itself,
+ * avoiding launching workers, just to throw error.
+ */
+ if (!IsParallelCopy())
+ CheckTargetRelValidity(cstate);
/*
* If the target file is new-in-transaction, we assume that checking FSM
@@ -3634,7 +4367,8 @@ CopyFrom(CopyState cstate)
target_resultRelInfo = resultRelInfo;
/* Verify the named relation is a valid target for INSERT */
- CheckValidResultRel(resultRelInfo, CMD_INSERT);
+ if (!IsParallelCopy())
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
ExecOpenIndices(resultRelInfo, false);
@@ -3783,13 +4517,16 @@ CopyFrom(CopyState cstate)
has_instead_insert_row_trig = (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_instead_row);
- /*
- * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
- * should do this for COPY, since it's not really an "INSERT" statement as
- * such. However, executing these triggers maintains consistency with the
- * EACH ROW triggers that we already fire on COPY.
- */
- ExecBSInsertTriggers(estate, resultRelInfo);
+ if (!IsParallelCopy())
+ {
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+ }
econtext = GetPerTupleExprContext(estate);
@@ -3889,6 +4626,16 @@ CopyFrom(CopyState cstate)
!has_instead_insert_row_trig &&
resultRelInfo->ri_FdwRoutine == NULL;
+ /*
+ * If the table has any partitions that are either foreign or
+ * has BEFORE/INSTEAD OF triggers, we can't perform copy
+ * operations with parallel workers.
+ */
+ if (!leafpart_use_multi_insert && IsParallelWorker())
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot perform PARALLEL COPY if partition has BEFORE/INSTEAD OF triggers, or if the partition is foreign partition"),
+ errhint("Try COPY without PARALLEL option")));
+
/* Set the multi-insert buffer to use for this partition. */
if (leafpart_use_multi_insert)
{
@@ -4305,7 +5052,7 @@ BeginCopyFrom(ParseState *pstate,
* only in text mode.
*/
initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf = (IsParallelCopy()) ? NULL : (char *) palloc(RAW_BUF_SIZE + 1);
cstate->raw_buf_index = cstate->raw_buf_len = 0;
if (!cstate->binary)
{
@@ -4454,26 +5201,35 @@ NextCopyFromRawFields(CopyState cstate, char ***fields, int *nfields)
/* only available for text or csv input */
Assert(!cstate->binary);
- /* on input just throw the header line away */
- if (cstate->cur_lineno == 0 && cstate->header_line)
+ if (IsParallelCopy())
{
- cstate->cur_lineno++;
- if (CopyReadLine(cstate))
- return false; /* done */
+ done = GetWorkerLine(cstate);
+ if (done && cstate->line_buf.len == 0)
+ return false;
}
+ else
+ {
+ /* on input just throw the header line away */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ return false; /* done */
+ }
- cstate->cur_lineno++;
+ cstate->cur_lineno++;
- /* Actually read the line into memory here */
- done = CopyReadLine(cstate);
+ /* Actually read the line into memory here */
+ done = CopyReadLine(cstate);
- /*
- * EOF at start of line means we're done. If we see EOF after some
- * characters, we act as though it was newline followed by EOF, ie,
- * process the line and then exit loop on next iteration.
- */
- if (done && cstate->line_buf.len == 0)
- return false;
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ return false;
+ }
/* Parse the line into de-escaped field values */
if (cstate->csv_mode)
@@ -4721,9 +5477,31 @@ CopyReadLine(CopyState cstate)
*/
if (cstate->copy_dest == COPY_NEW_FE)
{
+ bool bIsFirst = true;
do
{
- cstate->raw_buf_index = cstate->raw_buf_len;
+ if (!IsParallelCopy())
+ cstate->raw_buf_index = cstate->raw_buf_len;
+ else
+ {
+ if (cstate->raw_buf_index == RAW_BUF_SIZE)
+ {
+ /* Get a new block if it is the first time, From the
+ * subsequent time, reset the index and re-use the same
+ * block.
+ */
+ if (bIsFirst)
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ uint32 block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ cstate->raw_buf = pcshared_info->data_blocks[block_pos].data;
+ bIsFirst = false;
+ }
+
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ }
+ }
+
} while (CopyLoadRawBuf(cstate));
}
}
@@ -4778,7 +5556,7 @@ static void
ConvertToServerEncoding(CopyState cstate)
{
/* Done reading the line. Convert it to server encoding. */
- if (cstate->need_transcoding)
+ if (cstate->need_transcoding && (!IsParallelCopy() || IsWorker()))
{
char *cvt;
cvt = pg_any_to_server(cstate->line_buf.data,
@@ -4817,6 +5595,11 @@ CopyReadLineText(CopyState cstate)
char quotec = '\0';
char escapec = '\0';
+ /* For parallel copy */
+ int line_size = 0;
+ int line_pos = 0;
+
+ cstate->eol_type = EOL_UNKNOWN;
if (cstate->csv_mode)
{
quotec = cstate->quote[0];
@@ -4871,6 +5654,8 @@ CopyReadLineText(CopyState cstate)
if (raw_buf_ptr >= copy_buf_len || need_data)
{
REFILL_LINEBUF;
+ SetRawBufForLoad(cstate, line_size, copy_buf_len, raw_buf_ptr,
+ ©_raw_buf);
/*
* Try to read some more data. This will certainly reset
@@ -5095,9 +5880,15 @@ CopyReadLineText(CopyState cstate)
* discard the data and the \. sequence.
*/
if (prev_raw_ptr > cstate->raw_buf_index)
- appendBinaryStringInfo(&cstate->line_buf,
+ {
+ if (!IsParallelCopy())
+ appendBinaryStringInfo(&cstate->line_buf,
cstate->raw_buf + cstate->raw_buf_index,
prev_raw_ptr - cstate->raw_buf_index);
+ else
+ line_size += prev_raw_ptr - cstate->raw_buf_index;
+ }
+
cstate->raw_buf_index = raw_buf_ptr;
result = true; /* report EOF */
break;
@@ -5149,6 +5940,26 @@ not_end_of_copy:
IF_NEED_REFILL_AND_EOF_BREAK(mblen - 1);
raw_buf_ptr += mblen - 1;
}
+
+ /*
+ * Skip the header line. Update the line here, this cannot be done at
+ * the beginning, as there is a possibility that file contains empty
+ * lines.
+ */
+ if (IsParallelCopy() && first_char_in_line && !IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 line_first_block = pcshared_info->cur_block_pos;
+ line_pos = UpdateBlockInLineInfo(cstate,
+ line_first_block,
+ cstate->raw_buf_index, -1,
+ LINE_LEADER_POPULATING);
+ lineInfo = &pcshared_info->line_boundaries.ring[line_pos];
+ elog(DEBUG1, "[Leader] Adding - block:%d, offset:%d, line position:%d",
+ line_first_block, lineInfo->start_offset, line_pos);
+ }
+
first_char_in_line = false;
} /* end of outer loop */
@@ -5157,6 +5968,7 @@ not_end_of_copy:
*/
REFILL_LINEBUF;
CLEAR_EOL_LINE();
+ EndLineParallelCopy(cstate, line_pos, line_size, raw_buf_ptr);
return result;
}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index c4af40b..c95db78 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2353,7 +2353,7 @@ psql_completion(const char *text, int start, int end)
/* Complete COPY <sth> FROM|TO filename WITH ( */
else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "("))
COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
- "HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
+ "HEADER", "PARALLEL", "QUOTE", "ESCAPE", "FORCE_QUOTE",
"FORCE_NOT_NULL", "FORCE_NULL", "ENCODING");
/* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 5348011..4ea02f7 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -381,6 +381,8 @@ extern FullTransactionId GetTopFullTransactionId(void);
extern FullTransactionId GetTopFullTransactionIdIfAny(void);
extern FullTransactionId GetCurrentFullTransactionId(void);
extern FullTransactionId GetCurrentFullTransactionIdIfAny(void);
+extern void AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId);
+extern void AssignCommandIdForWorker(CommandId commandId, bool used);
extern void MarkCurrentTransactionIdLoggedIfAny(void);
extern bool SubTransactionIsActive(SubTransactionId subxid);
extern CommandId GetCurrentCommandId(bool used);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 3a83d0f..ba93f65 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1704,6 +1704,7 @@ ParallelCopyLineBoundary
ParallelCopyData
ParallelCopyDataBlock
ParallelCopyLineBuf
+ParallelCopyLineState
ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
--
1.8.3.1
[text/x-patch] v2-0004-Documentation-for-parallel-copy.patch (2.0K, ../../CALDaNm2EYd67r3NwScaFh9_onbX_vpKJVS9p-=+TX22q47m+Zg@mail.gmail.com/6-v2-0004-Documentation-for-parallel-copy.patch)
download | inline diff:
From e543e3b3def9367bd30191ea334c26fb7e372b96 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:00:36 +0530
Subject: [PATCH v2 4/5] Documentation for parallel copy.
This patch has the documentation changes for parallel copy.
---
doc/src/sgml/ref/copy.sgml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index 18189ab..95d349d 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -37,6 +37,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
DELIMITER '<replaceable class="parameter">delimiter_character</replaceable>'
NULL '<replaceable class="parameter">null_string</replaceable>'
HEADER [ <replaceable class="parameter">boolean</replaceable> ]
+ PARALLEL <replaceable class="parameter">integer</replaceable>
QUOTE '<replaceable class="parameter">quote_character</replaceable>'
ESCAPE '<replaceable class="parameter">escape_character</replaceable>'
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
@@ -275,6 +276,21 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
</varlistentry>
<varlistentry>
+ <term><literal>PARALLEL</literal></term>
+ <listitem>
+ <para>
+ Perform <command>COPY FROM</command> in parallel using <replaceable
+ class="parameter"> integer</replaceable> background workers. Please
+ note that it is not guaranteed that the number of parallel workers
+ specified in <replaceable class="parameter">integer</replaceable> will
+ be used during execution. It is possible for a copy to run with fewer
+ workers than specified, or even with no workers at all. This option is
+ allowed only in <command>COPY FROM</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>QUOTE</literal></term>
<listitem>
<para>
--
1.8.3.1
[text/x-patch] v2-0005-Tests-for-parallel-copy.patch (19.8K, ../../CALDaNm2EYd67r3NwScaFh9_onbX_vpKJVS9p-=+TX22q47m+Zg@mail.gmail.com/7-v2-0005-Tests-for-parallel-copy.patch)
download | inline diff:
From ec2489bc2316da2aaa959e1e1c53df1f0a801d8a Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:19:39 +0530
Subject: [PATCH v2 5/5] Tests for parallel copy.
This patch has the tests for parallel copy.
---
src/test/regress/expected/copy2.out | 206 ++++++++++++++++++++++++++++++++++-
src/test/regress/input/copy.source | 12 +++
src/test/regress/output/copy.source | 12 +++
src/test/regress/sql/copy2.sql | 208 +++++++++++++++++++++++++++++++++++-
4 files changed, 430 insertions(+), 8 deletions(-)
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index e40287d..0e01fa0 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -254,18 +254,32 @@ It is "perfect".|
"It is ""perfect""."," "
"",
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+SELECT * FROM testnl;
+ a | b | c
+---+----------------------+---
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+(2 rows)
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
COPY testeoc TO stdout CSV;
a\.
\.b
c\.d
"\."
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
1 \\0
@@ -280,6 +294,15 @@ SELECT * FROM testnull;
|
(4 rows)
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+SELECT * FROM testnull;
+ a | b
+----+----
+ 42 | \0
+ |
+(2 rows)
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -349,6 +372,34 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ a2
+ b
+(2 rows)
+
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+COMMIT;
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
SELECT * FROM vistest;
a
@@ -409,7 +460,7 @@ SELECT * FROM vistest;
(2 rows)
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -420,6 +471,8 @@ CREATE TEMP TABLE forcetest (
-- should succeed with no effect ("b" remains an empty string, "c" remains NULL)
BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
b | c
@@ -430,6 +483,8 @@ SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
c | d
@@ -486,6 +541,31 @@ select * from check_con_tbl;
(2 rows)
+\d+ check_con_tbl
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | | | plain | |
+Check constraints:
+ "check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
+
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":1}
+NOTICE: input = {"f1":null}
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":0}
+ERROR: new row for relation "check_con_tbl" violates check constraint "check_con_tbl_check"
+DETAIL: Failing row contains (0).
+CONTEXT: COPY check_con_tbl, line 1: "0"
+parallel worker
+select * from check_con_tbl;
+ f1
+----
+ 1
+
+(2 rows)
+
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
CREATE ROLE regress_rls_copy_user_colperms;
@@ -600,8 +680,126 @@ SELECT * FROM instead_of_insert_tbl;
(2 rows)
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+ERROR: argument to option "parallel" must be a positive integer greater than zero
+LINE 1: COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0'...
+ ^
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+COPY temp_test (a) from stdin with (PARALLEL 1);
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+ERROR: column "xyz" of relation "test_parallel_copy" does not exist
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+ERROR: column "d" specified more than once
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: invalid input syntax for type integer: ""
+CONTEXT: COPY test_parallel_copy, line 0, column a: ""
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2000 230 23 23"
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2001 231 \N \N"
+parallel worker
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: extra data after last expected column
+CONTEXT: COPY test_parallel_copy, line 1: "2002 232 40 50 60 70 80"
+parallel worker
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ERROR: column "f" does not exist
+LINE 1: ..._parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ERROR: missing FROM-clause entry for table "x"
+LINE 1: ...rallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+ERROR: cannot use subquery in COPY FROM WHERE condition
+LINE 1: ...arallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT...
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+ERROR: set-returning functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...lel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_s...
+ ^
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+ERROR: window functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...rallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number...
+ ^
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+ a | b | c | d | e
+-------+----+------------+---------+---------
+ 1 | 11 | test_c1 | test_d1 | test_e1
+ 2 | 12 | test_c2 | test_d2 | test_e2
+ | 3 | stuff | test_d3 |
+ | 4 | stuff | test_d4 |
+ | 5 | stuff | test_d5 |
+ | | 45 | 80 | 90
+ | | x | \x | \x
+ | | , | \, | \
+ 3000 | | c | |
+ 4000 | | C | |
+ 4001 | 1 | empty | |
+ 4002 | 2 | null | |
+ 4003 | 3 | Backslash | \ | \
+ 4004 | 4 | BackslashX | \X | \X
+ 4005 | 5 | N | N | N
+ 4006 | 6 | BackslashN | \N | \N
+ 4007 | 7 | XX | XX | XX
+ 4008 | 8 | Delimiter | : | :
+ 50004 | 25 | 35 | 45 | 55
+ 60004 | 25 | 35 | 45 | 55
+ 60005 | 26 | 36 | 46 | 56
+(21 rows)
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source
index a1d529a..159c058 100644
--- a/src/test/regress/input/copy.source
+++ b/src/test/regress/input/copy.source
@@ -15,6 +15,13 @@ DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+
+SELECT COUNT(*) FROM tenk1;
+
+TRUNCATE tenk1;
+
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
@@ -159,6 +166,11 @@ truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
+
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source
index 938d355..c3003fe 100644
--- a/src/test/regress/output/copy.source
+++ b/src/test/regress/output/copy.source
@@ -9,6 +9,15 @@ COPY onek FROM '@abs_srcdir@/data/onek.data';
COPY onek TO '@abs_builddir@/results/onek.data';
DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+SELECT COUNT(*) FROM tenk1;
+ count
+-------
+ 10000
+(1 row)
+
+TRUNCATE tenk1;
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
COPY person FROM '@abs_srcdir@/data/person.data';
@@ -113,6 +122,9 @@ insert into parted_copytest select x,1,'One' from generate_series(1011,1020) x;
copy (select * from parted_copytest order by a) to '@abs_builddir@/results/parted_copytest.csv';
truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql
index 902f4fa..7015698 100644
--- a/src/test/regress/sql/copy2.sql
+++ b/src/test/regress/sql/copy2.sql
@@ -157,7 +157,7 @@ COPY y TO stdout (FORMAT CSV, FORCE_QUOTE *);
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
1,"a field with two LFs
@@ -165,8 +165,16 @@ COPY testnl FROM stdin CSV;
inside",2
\.
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+1,"a field with two LFs
+
+inside",2
+\.
+
+SELECT * FROM testnl;
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
a\.
@@ -175,11 +183,19 @@ c\.d
"\."
\.
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+a\.
+\.b
+c\.d
+"\."
+\.
+
COPY testeoc TO stdout CSV;
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
@@ -191,6 +207,14 @@ COPY testnull FROM stdin WITH NULL AS E'\\0';
SELECT * FROM testnull;
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+42 \\0
+\0 \0
+\.
+
+SELECT * FROM testnull;
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -235,6 +259,23 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+a2
+b
+\.
+SELECT * FROM vistest;
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+d2
+e
+\.
+SELECT * FROM vistest;
+COMMIT;
+SELECT * FROM vistest;
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
x
y
@@ -284,7 +325,7 @@ SELECT * FROM vistest;
COMMIT;
SELECT * FROM vistest;
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -297,6 +338,10 @@ BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
1,,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
+1,,""
+\.
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
@@ -304,6 +349,10 @@ BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
2,'a',,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
+2,'a',,""
+\.
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
-- should fail with not-null constraint violation
@@ -339,6 +388,16 @@ copy check_con_tbl from stdin;
0
\.
select * from check_con_tbl;
+\d+ check_con_tbl
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+1
+\N
+\.
+copy check_con_tbl from stdin with (parallel 1);
+0
+\.
+select * from check_con_tbl;
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
@@ -440,8 +499,149 @@ test1
SELECT * FROM instead_of_insert_tbl;
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+1 11 test_c1 test_d1 test_e1
+2 12 test_c2 test_d2 test_e2
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+3 test_d3
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+4 test_d4
+5 test_d5
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+b d
+\.
+
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+10
+\.
+
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+60,60,60
+\.
+
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+60
+\.
+
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+
+COPY temp_test (a) from stdin with (PARALLEL 1);
+10
+\.
+
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2000 230 23 23
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2001 231 \N \N
+\.
+
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2002 232 40 50 60 70 80
+\.
+
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+x,45,80,90
+x,\x,\\x,\\\x
+x,\,,\\\,,\\
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+3000;;c;;
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+4000:\X:C:\X:\X
+4001:1:empty::
+4002:2:null:\X:\X
+4003:3:Backslash:\\:\\
+4004:4:BackslashX:\\X:\\X
+4005:5:N:\N:\N
+4006:6:BackslashN:\\N:\\N
+4007:7:XX:\XX:\XX
+4008:8:Delimiter:\::\:
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+50003 24 34 44 54
+50004 25 35 45 55
+50005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+60001 22 32 42 52
+60002 23 33 43 53
+60003 24 34 44 54
+60004 25 35 45 55
+60005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
--
1.8.3.1
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-03 07:03 Bharath Rupireddy <[email protected]>
parent: vignesh C <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Bharath Rupireddy @ 2020-08-03 07:03 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: Amit Kapila <[email protected]>; Ashutosh Sharma <[email protected]>; Rafia Sabih <[email protected]>; Andres Freund <[email protected]>; Robert Haas <[email protected]>; Ants Aasma <[email protected]>; Tomas Vondra <[email protected]>; Alastair Turner <[email protected]>; Thomas Munro <[email protected]>; PostgreSQL Hackers <[email protected]>
On Sat, Aug 1, 2020 at 9:55 AM vignesh C <[email protected]> wrote:
>
> The patches were not applying because of the recent commits.
> I have rebased the patch over head & attached.
>
I rebased v2-0006-Parallel-Copy-For-Binary-Format-Files.patch.
Putting together all the patches rebased on to the latest commit
b8fdee7d0ca8bd2165d46fb1468f75571b706a01. Patches from 0001 to 0005
are rebased by Vignesh, that are from the previous mail and the patch
0006 is rebased by me.
Please consider this patch set for further review.
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
Attachments:
[application/x-patch] v2-0001-Copy-code-readjustment-to-support-parallel-copy.patch (16.6K, ../../CALj2ACUKKWnju+Xbm1tOtDQj=FxSVBZ1acZcF77RWuN6MCDHxg@mail.gmail.com/2-v2-0001-Copy-code-readjustment-to-support-parallel-copy.patch)
download | inline diff:
From f2ba043af005c55961ed68c9a595cee58c46c79d Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 08:52:48 +0530
Subject: [PATCH v2 1/5] Copy code readjustment to support parallel copy.
This patch has the copy code slightly readjusted so that the common code is
separated to functions/macros, these functions/macros will be used by the
workers in the parallel copy code of the upcoming patches. EOL removal is moved
from CopyReadLine to CopyReadLineText, this change was required because in case
of parallel copy the record identification and record updation is done in
CopyReadLineText, before record information is updated in shared memory the new
line characters should be removed.
---
src/backend/commands/copy.c | 361 ++++++++++++++++++++++++++------------------
1 file changed, 218 insertions(+), 143 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index db7d24a..3efafca 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -95,6 +95,9 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
+
/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
@@ -224,7 +227,6 @@ typedef struct CopyStateData
* appropriate amounts of data from this buffer. In both modes, we
* guarantee that there is a \0 at raw_buf[raw_buf_len].
*/
-#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
@@ -354,6 +356,27 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/*
+ * CLEAR_EOL_LINE - Wrapper for clearing EOL.
+ */
+#define CLEAR_EOL_LINE() \
+if (!result && !IsHeaderLine()) \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len) \
+
+/*
+ * INCREMENTPROCESSED - Increment the lines processed.
+ */
+#define INCREMENTPROCESSED(processed) \
+processed++;
+
+/*
+ * GETPROCESSED - Get the lines processed.
+ */
+#define GETPROCESSED(processed) \
+return processed;
+
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -401,7 +424,11 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
-
+static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
+ List *attnamelist);
+static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size);
+static void ConvertToServerEncoding(CopyState cstate);
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -801,14 +828,18 @@ CopyLoadRawBuf(CopyState cstate)
{
int nbytes = RAW_BUF_BYTES(cstate);
int inbytes;
+ int minread = 1;
/* Copy down the unprocessed data if any. */
if (nbytes > 0)
memmove(cstate->raw_buf, cstate->raw_buf + cstate->raw_buf_index,
nbytes);
+ if (cstate->copy_dest == COPY_NEW_FE)
+ minread = RAW_BUF_SIZE - nbytes;
+
inbytes = CopyGetData(cstate, cstate->raw_buf + nbytes,
- 1, RAW_BUF_SIZE - nbytes);
+ minread, RAW_BUF_SIZE - nbytes);
nbytes += inbytes;
cstate->raw_buf[nbytes] = '\0';
cstate->raw_buf_index = 0;
@@ -1514,7 +1545,6 @@ BeginCopy(ParseState *pstate,
{
CopyState cstate;
TupleDesc tupDesc;
- int num_phys_attrs;
MemoryContext oldcontext;
/* Allocate workspace and zero all fields */
@@ -1680,6 +1710,24 @@ BeginCopy(ParseState *pstate,
tupDesc = cstate->queryDesc->tupDesc;
}
+ PopulateCommonCstateInfo(cstate, tupDesc, attnamelist);
+ cstate->copy_dest = COPY_FILE; /* default */
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return cstate;
+}
+
+/*
+ * PopulateCommonCstateInfo - Populates the common variables required for copy
+ * from operation. This is a helper function for BeginCopy function.
+ */
+static void
+PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc,
+ List *attnamelist)
+{
+ int num_phys_attrs;
+
/* Generate or convert list of attributes to process */
cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
@@ -1799,12 +1847,6 @@ BeginCopy(ParseState *pstate,
pg_database_encoding_max_length() > 1);
/* See Multibyte encoding comment above */
cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->file_encoding);
-
- cstate->copy_dest = COPY_FILE; /* default */
-
- MemoryContextSwitchTo(oldcontext);
-
- return cstate;
}
/*
@@ -2696,32 +2738,11 @@ CopyMultiInsertInfoStore(CopyMultiInsertInfo *miinfo, ResultRelInfo *rri,
}
/*
- * Copy FROM file to relation.
+ * Check if the relation specified in copy from is valid.
*/
-uint64
-CopyFrom(CopyState cstate)
+static void
+CheckTargetRelValidity(CopyState cstate)
{
- ResultRelInfo *resultRelInfo;
- ResultRelInfo *target_resultRelInfo;
- ResultRelInfo *prevResultRelInfo = NULL;
- EState *estate = CreateExecutorState(); /* for ExecConstraints() */
- ModifyTableState *mtstate;
- ExprContext *econtext;
- TupleTableSlot *singleslot = NULL;
- MemoryContext oldcontext = CurrentMemoryContext;
-
- PartitionTupleRouting *proute = NULL;
- ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
- int ti_options = 0; /* start with default options for insert */
- BulkInsertState bistate = NULL;
- CopyInsertMethod insertMethod;
- CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
- uint64 processed = 0;
- bool has_before_insert_row_trig;
- bool has_instead_insert_row_trig;
- bool leafpart_use_multi_insert = false;
-
Assert(cstate->rel);
/*
@@ -2758,27 +2779,6 @@ CopyFrom(CopyState cstate)
RelationGetRelationName(cstate->rel))));
}
- /*
- * If the target file is new-in-transaction, we assume that checking FSM
- * for free space is a waste of time. This could possibly be wrong, but
- * it's unlikely.
- */
- if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
- (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
- cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
- ti_options |= TABLE_INSERT_SKIP_FSM;
-
- /*
- * Optimize if new relfilenode was created in this subxact or one of its
- * committed children and we won't see those rows later as part of an
- * earlier scan or command. The subxact test ensures that if this subxact
- * aborts then the frozen rows won't be visible after xact cleanup. Note
- * that the stronger test of exactly which subtransaction created it is
- * crucial for correctness of this optimization. The test for an earlier
- * scan or command tolerates false negatives. FREEZE causes other sessions
- * to see rows they would not see under MVCC, and a false negative merely
- * spreads that anomaly to the current session.
- */
if (cstate->freeze)
{
/*
@@ -2816,9 +2816,61 @@ CopyFrom(CopyState cstate)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot perform COPY FREEZE because the table was not created or truncated in the current subtransaction")));
+ }
+}
+
+/*
+ * Copy FROM file to relation.
+ */
+uint64
+CopyFrom(CopyState cstate)
+{
+ ResultRelInfo *resultRelInfo;
+ ResultRelInfo *target_resultRelInfo;
+ ResultRelInfo *prevResultRelInfo = NULL;
+ EState *estate = CreateExecutorState(); /* for ExecConstraints() */
+ ModifyTableState *mtstate;
+ ExprContext *econtext;
+ TupleTableSlot *singleslot = NULL;
+ MemoryContext oldcontext = CurrentMemoryContext;
+
+ PartitionTupleRouting *proute = NULL;
+ ErrorContextCallback errcallback;
+ CommandId mycid = GetCurrentCommandId(true);
+ int ti_options = 0; /* start with default options for insert */
+ BulkInsertState bistate = NULL;
+ CopyInsertMethod insertMethod;
+ CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
+ uint64 processed = 0;
+ bool has_before_insert_row_trig;
+ bool has_instead_insert_row_trig;
+ bool leafpart_use_multi_insert = false;
+
+ CheckTargetRelValidity(cstate);
+ /*
+ * If the target file is new-in-transaction, we assume that checking FSM
+ * for free space is a waste of time. This could possibly be wrong, but
+ * it's unlikely.
+ */
+ if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
+ (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
+ cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
+ ti_options |= TABLE_INSERT_SKIP_FSM;
+
+ /*
+ * Optimize if new relfilenode was created in this subxact or one of its
+ * committed children and we won't see those rows later as part of an
+ * earlier scan or command. The subxact test ensures that if this subxact
+ * aborts then the frozen rows won't be visible after xact cleanup. Note
+ * that the stronger test of exactly which subtransaction created it is
+ * crucial for correctness of this optimization. The test for an earlier
+ * scan or command tolerates false negatives. FREEZE causes other sessions
+ * to see rows they would not see under MVCC, and a false negative merely
+ * spreads that anomaly to the current session.
+ */
+ if (cstate->freeze)
ti_options |= TABLE_INSERT_FROZEN;
- }
/*
* We need a ResultRelInfo so we can use the regular executor's
@@ -3311,7 +3363,7 @@ CopyFrom(CopyState cstate)
* or FDW; this is the same definition used by nodeModifyTable.c
* for counting tuples inserted by an INSERT command.
*/
- processed++;
+ INCREMENTPROCESSED(processed)
}
}
@@ -3366,30 +3418,15 @@ CopyFrom(CopyState cstate)
FreeExecutorState(estate);
- return processed;
+ GETPROCESSED(processed)
}
/*
- * Setup to read tuples from a file for COPY FROM.
- *
- * 'rel': Used as a template for the tuples
- * 'filename': Name of server-local file to read
- * 'attnamelist': List of char *, columns to include. NIL selects all cols.
- * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
- *
- * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ * PopulateCstateCatalogInfo - populate the catalog information.
*/
-CopyState
-BeginCopyFrom(ParseState *pstate,
- Relation rel,
- const char *filename,
- bool is_program,
- copy_data_source_cb data_source_cb,
- List *attnamelist,
- List *options)
+static void
+PopulateCstateCatalogInfo(CopyState cstate)
{
- CopyState cstate;
- bool pipe = (filename == NULL);
TupleDesc tupDesc;
AttrNumber num_phys_attrs,
num_defaults;
@@ -3399,38 +3436,8 @@ BeginCopyFrom(ParseState *pstate,
Oid in_func_oid;
int *defmap;
ExprState **defexprs;
- MemoryContext oldcontext;
bool volatile_defexprs;
- cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
- oldcontext = MemoryContextSwitchTo(cstate->copycontext);
-
- /* Initialize state variables */
- cstate->reached_eof = false;
- cstate->eol_type = EOL_UNKNOWN;
- cstate->cur_relname = RelationGetRelationName(cstate->rel);
- cstate->cur_lineno = 0;
- cstate->cur_attname = NULL;
- cstate->cur_attval = NULL;
-
- /*
- * Set up variables to avoid per-attribute overhead. attribute_buf and
- * raw_buf are used in both text and binary modes, but we use line_buf
- * only in text mode.
- */
- initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
- cstate->raw_buf_index = cstate->raw_buf_len = 0;
- if (!cstate->binary)
- {
- initStringInfo(&cstate->line_buf);
- cstate->line_buf_converted = false;
- }
-
- /* Assign range table, we'll need it in CopyFrom. */
- if (pstate)
- cstate->range_table = pstate->p_rtable;
-
tupDesc = RelationGetDescr(cstate->rel);
num_phys_attrs = tupDesc->natts;
num_defaults = 0;
@@ -3508,6 +3515,61 @@ BeginCopyFrom(ParseState *pstate,
cstate->defexprs = defexprs;
cstate->volatile_defexprs = volatile_defexprs;
cstate->num_defaults = num_defaults;
+}
+
+/*
+ * Setup to read tuples from a file for COPY FROM.
+ *
+ * 'rel': Used as a template for the tuples
+ * 'filename': Name of server-local file to read
+ * 'attnamelist': List of char *, columns to include. NIL selects all cols.
+ * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
+ *
+ * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ */
+CopyState
+BeginCopyFrom(ParseState *pstate,
+ Relation rel,
+ const char *filename,
+ bool is_program,
+ copy_data_source_cb data_source_cb,
+ List *attnamelist,
+ List *options)
+{
+ CopyState cstate;
+ bool pipe = (filename == NULL);
+ MemoryContext oldcontext;
+
+ cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ /* Initialize state variables */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /*
+ * Set up variables to avoid per-attribute overhead. attribute_buf is
+ * used in both text and binary modes, but we use line_buf and raw_buf
+ * only in text mode.
+ */
+ initStringInfo(&cstate->attribute_buf);
+ cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ if (!cstate->binary)
+ {
+ initStringInfo(&cstate->line_buf);
+ cstate->line_buf_converted = false;
+ }
+
+ /* Assign range table, we'll need it in CopyFrom. */
+ if (pstate)
+ cstate->range_table = pstate->p_rtable;
+
+ PopulateCstateCatalogInfo(cstate);
cstate->is_program = is_program;
if (data_source_cb)
@@ -3917,45 +3979,60 @@ CopyReadLine(CopyState cstate)
} while (CopyLoadRawBuf(cstate));
}
}
- else
- {
- /*
- * If we didn't hit EOF, then we must have transferred the EOL marker
- * to line_buf along with the data. Get rid of it.
- */
- switch (cstate->eol_type)
- {
- case EOL_NL:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CR:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\r');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CRNL:
- Assert(cstate->line_buf.len >= 2);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 2] == '\r');
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len -= 2;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_UNKNOWN:
- /* shouldn't get here */
- Assert(false);
- break;
- }
- }
+ ConvertToServerEncoding(cstate);
+ return result;
+}
+
+/*
+ * ClearEOLFromCopiedData - Clear EOL from the copied data.
+ */
+static void
+ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size)
+{
+ /*
+ * If we didn't hit EOF, then we must have transferred the EOL marker
+ * to line_buf along with the data. Get rid of it.
+ */
+ switch (cstate->eol_type)
+ {
+ case EOL_NL:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CR:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\r');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CRNL:
+ Assert(*copy_line_size >= 2);
+ Assert(copy_line_data[copy_line_pos - 2] == '\r');
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 2] = '\0';
+ *copy_line_size -= 2;
+ break;
+ case EOL_UNKNOWN:
+ /* shouldn't get here */
+ Assert(false);
+ break;
+ }
+}
+
+/*
+ * ConvertToServerEncoding - convert contents to server encoding.
+ */
+static void
+ConvertToServerEncoding(CopyState cstate)
+{
/* Done reading the line. Convert it to server encoding. */
if (cstate->need_transcoding)
{
char *cvt;
-
cvt = pg_any_to_server(cstate->line_buf.data,
cstate->line_buf.len,
cstate->file_encoding);
@@ -3967,11 +4044,8 @@ CopyReadLine(CopyState cstate)
pfree(cvt);
}
}
-
/* Now it's safe to use the buffer in error messages */
cstate->line_buf_converted = true;
-
- return result;
}
/*
@@ -4334,6 +4408,7 @@ not_end_of_copy:
* Transfer any still-uncopied data to line_buf.
*/
REFILL_LINEBUF;
+ CLEAR_EOL_LINE();
return result;
}
--
1.8.3.1
[application/x-patch] v2-0002-Framework-for-leader-worker-in-parallel-copy.patch (31.5K, ../../CALj2ACUKKWnju+Xbm1tOtDQj=FxSVBZ1acZcF77RWuN6MCDHxg@mail.gmail.com/3-v2-0002-Framework-for-leader-worker-in-parallel-copy.patch)
download | inline diff:
From 1bf7d74c7308e6eae807c0258322abbb3890aede Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 08:56:39 +0530
Subject: [PATCH v2 2/5] Framework for leader/worker in parallel copy
This patch has the framework for data structures in parallel copy, leader
initialization, worker initialization, shared memory updation, starting workers,
wait for workers and workers exiting.
---
src/backend/access/transam/parallel.c | 4 +
src/backend/commands/copy.c | 756 +++++++++++++++++++++++++++++++++-
src/include/commands/copy.h | 2 +
src/tools/pgindent/typedefs.list | 7 +
4 files changed, 765 insertions(+), 4 deletions(-)
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index b042696..a3cff4b 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -25,6 +25,7 @@
#include "catalog/pg_enum.h"
#include "catalog/storage.h"
#include "commands/async.h"
+#include "commands/copy.h"
#include "executor/execParallel.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
@@ -145,6 +146,9 @@ static const struct
},
{
"parallel_vacuum_main", parallel_vacuum_main
+ },
+ {
+ "ParallelCopyMain", ParallelCopyMain
}
};
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3efafca..3dcdfc1 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -96,9 +96,180 @@ typedef enum CopyInsertMethod
} CopyInsertMethod;
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+
+/*
+ * The macros DATA_BLOCK_SIZE, RINGSIZE & MAX_BLOCKS_COUNT stores the records
+ * read from the file that need to be inserted into the relation. These values
+ * help in handover of multiple records with significant size of data to be
+ * processed by each of the workers to make sure there is no context switch & the
+ * work is fairly distributed among the workers. This number showed best
+ * results in the performance tests.
+ */
+#define DATA_BLOCK_SIZE RAW_BUF_SIZE
+
+/* It can hold upto 10000 record information for worker to process. */
+#define RINGSIZE (10 * 1000)
+
+/* It can hold 1000 blocks of 64K data in DSM to be processed by the worker. */
+#define MAX_BLOCKS_COUNT 1000
+
+/*
+ * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
+ * block to process to avoid lock contention. This value should be mode of
+ * RINGSIZE, as wrap around cases is currently not handled while selecting the
+ * WORKER_CHUNK_COUNT by the worker.
+ */
+#define WORKER_CHUNK_COUNT 50
+
+#define IsParallelCopy() (cstate->is_parallel)
+#define IsLeader() (cstate->pcdata->is_leader)
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
+ * Copy data block information.
+ * ParallelCopyDataBlock's will be created in DSM. Data read from file will be
+ * copied in these DSM data blocks. The leader process identifies the records
+ * and the record information will be shared to the workers. The workers will
+ * insert the records into the table. There can be one or more number of records
+ * in each of the data block based on the record size.
+ */
+typedef struct ParallelCopyDataBlock
+{
+ /* The number of unprocessed lines in the current block. */
+ pg_atomic_uint32 unprocessed_line_parts;
+
+ /*
+ * If the current line data is continued into another block, following_block
+ * will have the position where the remaining data need to be read.
+ */
+ uint32 following_block;
+
+ /*
+ * This flag will be set, when the leader finds out this block can be read
+ * safely by the worker. This helps the worker to start processing the line
+ * early where the line will be spread across many blocks and the worker
+ * need not wait for the complete line to be processed.
+ */
+ bool curr_blk_completed;
+ char data[DATA_BLOCK_SIZE]; /* data read from file */
+ uint8 skip_bytes;
+}ParallelCopyDataBlock;
+
+/*
+ * Individual line information.
+ * ParallelCopyLineBoundary is common data structure between leader & worker,
+ * Leader process will be populating data block, data block offset & the size of
+ * the record in DSM for the workers to copy the data into the relation.
+ * This is protected by the following sequence in the leader & worker. If they
+ * don't follow this order the worker might process wrong line_size and leader
+ * might populate the information which worker has not yet processed or in the
+ * process of processing.
+ * Leader should operate in the following order:
+ * 1) check if line_size is -1, if not wait, it means worker is still
+ * processing.
+ * 2) set line_state to LINE_LEADER_POPULATING.
+ * 3) update first_block, start_offset & cur_lineno in any order.
+ * 4) update line_size.
+ * 5) update line_state to LINE_LEADER_POPULATED.
+ * Worker should operate in the following order:
+ * 1) check line_state is LINE_LEADER_POPULATED, if not it means leader is still
+ * populating the data.
+ * 2) read line_size.
+ * 3) only one worker should choose one line for processing, this is handled by
+ * using pg_atomic_compare_exchange_u32, worker will change the sate to
+ * LINE_WORKER_PROCESSING only if line_state is LINE_LEADER_POPULATED.
+ * 4) read first_block, start_offset & cur_lineno in any order.
+ * 5) process line_size data.
+ * 6) update line_size to -1.
+ */
+typedef struct ParallelCopyLineBoundary
+{
+ /* Position of the first block in data_blocks array. */
+ uint32 first_block;
+ uint32 start_offset; /* start offset of the line */
+
+ /*
+ * Size of the current line -1 means line is yet to be filled completely,
+ * 0 means empty line, >0 means line filled with line size data.
+ */
+ pg_atomic_uint32 line_size;
+ pg_atomic_uint32 line_state; /* line state */
+ uint64 cur_lineno; /* line number for error messages */
+}ParallelCopyLineBoundary;
+
+/*
+ * Circular queue used to store the line information.
+ */
+typedef struct ParallelCopyLineBoundaries
+{
+ /* Position for the leader to populate a line. */
+ uint32 pos;
+
+ /* Data read from the file/stdin by the leader process. */
+ ParallelCopyLineBoundary ring[RINGSIZE];
+}ParallelCopyLineBoundaries;
+
+/*
+ * Shared information among parallel copy workers. This will be allocated in the
+ * DSM segment.
+ */
+typedef struct ParallelCopyShmInfo
+{
+ bool is_read_in_progress; /* file read status */
+
+ /*
+ * Actual lines inserted by worker, will not be same as
+ * total_worker_processed if where condition is specified along with copy.
+ * This will be the actual records inserted into the relation.
+ */
+ pg_atomic_uint64 processed;
+
+ /*
+ * The number of records currently processed by the worker, this will also
+ * include the number of records that was filtered because of where clause.
+ */
+ pg_atomic_uint64 total_worker_processed;
+ uint64 populated; /* lines populated by leader */
+ uint32 cur_block_pos; /* current data block */
+ ParallelCopyDataBlock data_blocks[MAX_BLOCKS_COUNT]; /* data block array */
+ FullTransactionId full_transaction_id; /* xid for copy from statement */
+ CommandId mycid; /* command id */
+ ParallelCopyLineBoundaries line_boundaries; /* line array */
+} ParallelCopyShmInfo;
+
+/*
+ * Parallel copy line buffer information.
+ */
+typedef struct ParallelCopyLineBuf
+{
+ StringInfoData line_buf;
+ uint64 cur_lineno; /* line number for error messages */
+}ParallelCopyLineBuf;
+
+/*
+ * Parallel copy data information.
+ */
+typedef struct ParallelCopyData
+{
+ Oid relid; /* relation id of the table */
+ ParallelCopyShmInfo *pcshared_info; /* common info in shared memory */
+ bool is_leader;
+
+ /* line position which worker is processing */
+ uint32 worker_processed_pos;
+
+ /*
+ * Local line_buf array, workers will copy it here and release the lines
+ * for the leader to continue.
+ */
+ ParallelCopyLineBuf worker_line_buf[WORKER_CHUNK_COUNT];
+ uint32 worker_line_buf_count; /* Number of lines */
+
+ /* Current position in worker_line_buf */
+ uint32 worker_line_buf_pos;
+}ParallelCopyData;
+
+/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
* even though some fields are used in only some cases.
@@ -230,10 +401,38 @@ typedef struct CopyStateData
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
+ int nworkers;
+ bool is_parallel;
+ ParallelCopyData *pcdata;
/* Shorthand for number of unconsumed bytes available in raw_buf */
#define RAW_BUF_BYTES(cstate) ((cstate)->raw_buf_len - (cstate)->raw_buf_index)
} CopyStateData;
+/*
+ * This structure helps in storing the common data from CopyStateData that are
+ * required by the workers. This information will then be allocated and stored
+ * into the DSM for the worker to retrieve and copy it to CopyStateData.
+ */
+typedef struct SerializedParallelCopyState
+{
+ /* low-level state data */
+ CopyDest copy_dest; /* type of copy source/destination */
+ int file_encoding; /* file or remote side's character encoding */
+ bool need_transcoding; /* file encoding diff from server? */
+ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */
+
+ /* parameters from the COPY command */
+ bool csv_mode; /* Comma Separated Value format? */
+ bool header_line; /* CSV header line? */
+ int null_print_len; /* length of same */
+ bool force_quote_all; /* FORCE_QUOTE *? */
+ bool convert_selectively; /* do selective binary conversion? */
+
+ /* Working state for COPY FROM */
+ AttrNumber num_defaults;
+ Oid relid;
+}SerializedParallelCopyState;
+
/* DestReceiver for COPY (query) TO */
typedef struct
{
@@ -263,6 +462,22 @@ typedef struct
/* Trim the list of buffers back down to this number after flushing */
#define MAX_PARTITION_BUFFERS 32
+/* DSM keys for parallel copy. */
+#define PARALLEL_COPY_KEY_SHARED_INFO 1
+#define PARALLEL_COPY_KEY_CSTATE 2
+#define PARALLEL_COPY_KEY_NULL_PRINT 3
+#define PARALLEL_COPY_KEY_DELIM 4
+#define PARALLEL_COPY_KEY_QUOTE 5
+#define PARALLEL_COPY_KEY_ESCAPE 6
+#define PARALLEL_COPY_KEY_ATTNAME_LIST 7
+#define PARALLEL_COPY_KEY_NOT_NULL_LIST 8
+#define PARALLEL_COPY_KEY_NULL_LIST 9
+#define PARALLEL_COPY_KEY_CONVERT_LIST 10
+#define PARALLEL_COPY_KEY_WHERE_CLAUSE_STR 11
+#define PARALLEL_COPY_KEY_RANGE_TABLE 12
+#define PARALLEL_COPY_WAL_USAGE 13
+#define PARALLEL_COPY_BUFFER_USAGE 14
+
/* Stores multi-insert data related to a single relation in CopyFrom. */
typedef struct CopyMultiInsertBuffer
{
@@ -424,11 +639,477 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
+static pg_attribute_always_inline void EndParallelCopy(ParallelContext *pcxt);
static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
- List *attnamelist);
+ List *attnamelist);
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
+
+/*
+ * SerializeParallelCopyState - Copy shared_cstate using cstate information.
+ */
+static pg_attribute_always_inline void
+SerializeParallelCopyState(CopyState cstate, SerializedParallelCopyState *shared_cstate)
+{
+ shared_cstate->copy_dest = cstate->copy_dest;
+ shared_cstate->file_encoding = cstate->file_encoding;
+ shared_cstate->need_transcoding = cstate->need_transcoding;
+ shared_cstate->encoding_embeds_ascii = cstate->encoding_embeds_ascii;
+ shared_cstate->csv_mode = cstate->csv_mode;
+ shared_cstate->header_line = cstate->header_line;
+ shared_cstate->null_print_len = cstate->null_print_len;
+ shared_cstate->force_quote_all = cstate->force_quote_all;
+ shared_cstate->convert_selectively = cstate->convert_selectively;
+ shared_cstate->num_defaults = cstate->num_defaults;
+ shared_cstate->relid = cstate->pcdata->relid;
+}
+
+/*
+ * RestoreString - Retrieve the string from shared memory.
+ */
+static void
+RestoreString(shm_toc *toc, int sharedkey, char **copystr)
+{
+ char *shared_str_val = (char *)shm_toc_lookup(toc, sharedkey, true);
+ if (shared_str_val)
+ *copystr = pstrdup(shared_str_val);
+}
+
+/*
+ * EstimateLineKeysStr - Estimate the size required in shared memory for the
+ * input string.
+ */
+static void
+EstimateLineKeysStr(ParallelContext *pcxt, char *inputstr)
+{
+ if (inputstr)
+ {
+ shm_toc_estimate_chunk(&pcxt->estimator, strlen(inputstr) + 1);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ }
+}
+
+/*
+ * SerializeString - Insert a string into shared memory.
+ */
+static void
+SerializeString(ParallelContext *pcxt, int key, char *inputstr)
+{
+ if (inputstr)
+ {
+ char *shmptr = (char *)shm_toc_allocate(pcxt->toc,
+ strlen(inputstr) + 1);
+ strcpy(shmptr, inputstr);
+ shm_toc_insert(pcxt->toc, key, shmptr);
+ }
+}
+
+/*
+ * PopulateParallelCopyShmInfo - set ParallelCopyShmInfo.
+ */
+static void
+PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
+ FullTransactionId full_transaction_id)
+{
+ uint32 count;
+
+ MemSet(shared_info_ptr, 0, sizeof(ParallelCopyShmInfo));
+ shared_info_ptr->is_read_in_progress = true;
+ shared_info_ptr->cur_block_pos = -1;
+ shared_info_ptr->full_transaction_id = full_transaction_id;
+ shared_info_ptr->mycid = GetCurrentCommandId(true);
+ for (count = 0; count < RINGSIZE; count++)
+ {
+ ParallelCopyLineBoundary *lineInfo = &shared_info_ptr->line_boundaries.ring[count];
+ pg_atomic_init_u32(&(lineInfo->line_size), -1);
+ }
+}
+
+/*
+ * BeginParallelCopy - start parallel copy tasks.
+ *
+ * Get the number of workers required to perform the parallel copy. The data
+ * structures that are required by the parallel workers will be initialized, the
+ * size required in DSM will be calculated and the necessary keys will be loaded
+ * in the DSM. The specified number of workers will then be launched.
+ */
+static ParallelContext*
+BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
+{
+ ParallelContext *pcxt;
+ ParallelCopyShmInfo *shared_info_ptr;
+ SerializedParallelCopyState *shared_cstate;
+ FullTransactionId full_transaction_id;
+ Size est_cstateshared;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ int parallel_workers = 0;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+ ParallelCopyData *pcdata;
+ MemoryContext oldcontext;
+
+ parallel_workers = Min(nworkers, max_worker_processes);
+
+ /* Can't perform copy in parallel */
+ if (parallel_workers <= 0)
+ return NULL;
+
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ MemoryContextSwitchTo(oldcontext);
+ cstate->pcdata = pcdata;
+
+ EnterParallelMode();
+ pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
+ parallel_workers);
+ Assert(pcxt->nworkers > 0);
+
+ /*
+ * Estimate size for shared information for PARALLEL_COPY_KEY_SHARED_INFO
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(ParallelCopyShmInfo));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_CSTATE */
+ est_cstateshared = MAXALIGN(sizeof(SerializedParallelCopyState));
+ shm_toc_estimate_chunk(&pcxt->estimator, est_cstateshared);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ EstimateLineKeysStr(pcxt, cstate->null_print);
+ EstimateLineKeysStr(pcxt, cstate->null_print_client);
+ EstimateLineKeysStr(pcxt, cstate->delim);
+ EstimateLineKeysStr(pcxt, cstate->quote);
+ EstimateLineKeysStr(pcxt, cstate->escape);
+
+ if (cstate->whereClause != NULL)
+ {
+ whereClauseStr = nodeToString(cstate->whereClause);
+ EstimateLineKeysStr(pcxt, whereClauseStr);
+ }
+
+ if (cstate->range_table != NULL)
+ {
+ rangeTableStr = nodeToString(cstate->range_table);
+ EstimateLineKeysStr(pcxt, rangeTableStr);
+ }
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_XID. */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(FullTransactionId));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_ATTNAME_LIST.
+ */
+ if (attnamelist != NIL)
+ {
+ attnameListStr = nodeToString(attnamelist);
+ EstimateLineKeysStr(pcxt, attnameListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NOT_NULL_LIST.
+ */
+ if (cstate->force_notnull != NIL)
+ {
+ notnullListStr = nodeToString(cstate->force_notnull);
+ EstimateLineKeysStr(pcxt, notnullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NULL_LIST.
+ */
+ if (cstate->force_null != NIL)
+ {
+ nullListStr = nodeToString(cstate->force_null);
+ EstimateLineKeysStr(pcxt, nullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_CONVERT_LIST.
+ */
+ if (cstate->convert_select != NIL)
+ {
+ convertListStr = nodeToString(cstate->convert_select);
+ EstimateLineKeysStr(pcxt, convertListStr);
+ }
+
+ /*
+ * Estimate space for WalUsage and BufferUsage -- PARALLEL_COPY_WAL_USAGE
+ * and PARALLEL_COPY_BUFFER_USAGE.
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ InitializeParallelDSM(pcxt);
+
+ /* If no DSM segment was available, back out (do serial copy) */
+ if (pcxt->seg == NULL)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /* Allocate shared memory for PARALLEL_COPY_KEY_SHARED_INFO */
+ shared_info_ptr = (ParallelCopyShmInfo *) shm_toc_allocate(pcxt->toc, sizeof(ParallelCopyShmInfo));
+ PopulateParallelCopyShmInfo(shared_info_ptr, full_transaction_id);
+
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_SHARED_INFO, shared_info_ptr);
+ pcdata->pcshared_info = shared_info_ptr;
+ pcdata->relid = relid;
+
+ /* Store shared build state, for which we reserved space. */
+ shared_cstate = (SerializedParallelCopyState *)shm_toc_allocate(pcxt->toc, est_cstateshared);
+
+ /* copy cstate variables. */
+ SerializeParallelCopyState(cstate, shared_cstate);
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_CSTATE, shared_cstate);
+
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ATTNAME_LIST, attnameListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NOT_NULL_LIST, notnullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_LIST, nullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_CONVERT_LIST, convertListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, whereClauseStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_RANGE_TABLE, rangeTableStr);
+
+ /*
+ * Allocate space for each worker's WalUsage and BufferUsage; no need to
+ * initialize.
+ */
+ walusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_WAL_USAGE, walusage);
+ bufferusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_BUFFER_USAGE, bufferusage);
+
+ LaunchParallelWorkers(pcxt);
+ if (pcxt->nworkers_launched == 0)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /*
+ * Caller needs to wait for all launched workers when we return. Make sure
+ * that the failure-to-start case will not hang forever.
+ */
+ WaitForParallelWorkersToAttach(pcxt);
+
+ pcdata->is_leader = true;
+ cstate->is_parallel = true;
+ return pcxt;
+}
+
+/*
+ * EndParallelCopy - end the parallel copy tasks.
+ */
+static pg_attribute_always_inline void
+EndParallelCopy(ParallelContext *pcxt)
+{
+ Assert(!IsParallelWorker());
+
+ DestroyParallelContext(pcxt);
+ ExitParallelMode();
+}
+
+/*
+ * InitializeParallelCopyInfo - Initialize parallel worker.
+ */
+static void
+InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
+ CopyState cstate, List *attnamelist)
+{
+ uint32 count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ cstate->copy_dest = shared_cstate->copy_dest;
+ cstate->file_encoding = shared_cstate->file_encoding;
+ cstate->need_transcoding = shared_cstate->need_transcoding;
+ cstate->encoding_embeds_ascii = shared_cstate->encoding_embeds_ascii;
+ cstate->csv_mode = shared_cstate->csv_mode;
+ cstate->header_line = shared_cstate->header_line;
+ cstate->null_print_len = shared_cstate->null_print_len;
+ cstate->force_quote_all = shared_cstate->force_quote_all;
+ cstate->convert_selectively = shared_cstate->convert_selectively;
+ cstate->num_defaults = shared_cstate->num_defaults;
+ pcdata->relid = shared_cstate->relid;
+
+ PopulateCommonCstateInfo(cstate, tup_desc, attnamelist);
+
+ /* Initialize state variables. */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /* Set up variables to avoid per-attribute overhead. */
+ initStringInfo(&cstate->attribute_buf);
+
+ initStringInfo(&cstate->line_buf);
+ for (count = 0; count < WORKER_CHUNK_COUNT; count++)
+ initStringInfo(&pcdata->worker_line_buf[count].line_buf);
+
+ cstate->line_buf_converted = false;
+ cstate->raw_buf = NULL;
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+}
+
+/*
+ * ParallelCopyMain - parallel copy worker's code.
+ *
+ * Where clause handling, convert tuple to columns, add default null values for
+ * the missing columns that are not present in that record. Find the partition
+ * if it is partitioned table, invoke before row insert Triggers, handle
+ * constraints and insert the tuples.
+ */
+void
+ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
+{
+ CopyState cstate;
+ ParallelCopyData *pcdata;
+ ParallelCopyShmInfo *pcshared_info;
+ SerializedParallelCopyState *shared_cstate;
+ Relation rel = NULL;
+ MemoryContext oldcontext;
+ List *attlist = NIL;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+
+ /* Allocate workspace and zero all fields. */
+ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData));
+
+ /*
+ * We allocate everything used by a cstate in a new memory context. This
+ * avoids memory leaks during repeated use of COPY in a query.
+ */
+ cstate->copycontext = AllocSetContextCreate(CurrentMemoryContext,
+ "COPY",
+ ALLOCSET_DEFAULT_SIZES);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ cstate->pcdata = pcdata;
+ pcdata->is_leader = false;
+ pcdata->worker_processed_pos = -1;
+ cstate->is_parallel = true;
+ pcshared_info = (ParallelCopyShmInfo *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_SHARED_INFO, false);
+
+ ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
+
+ pcdata->pcshared_info = pcshared_info;
+
+ shared_cstate = (SerializedParallelCopyState *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
+ cstate->null_print = (char *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_DELIM, &cstate->delim);
+ RestoreString(toc, PARALLEL_COPY_KEY_QUOTE, &cstate->quote);
+ RestoreString(toc, PARALLEL_COPY_KEY_ESCAPE, &cstate->escape);
+ RestoreString(toc, PARALLEL_COPY_KEY_ATTNAME_LIST, &attnameListStr);
+ if (attnameListStr)
+ attlist = (List *)stringToNode(attnameListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NOT_NULL_LIST, ¬nullListStr);
+ if (notnullListStr)
+ cstate->force_notnull = (List *)stringToNode(notnullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NULL_LIST, &nullListStr);
+ if (nullListStr)
+ cstate->force_null = (List *)stringToNode(nullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_CONVERT_LIST, &convertListStr);
+ if (convertListStr)
+ cstate->convert_select = (List *)stringToNode(convertListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, &whereClauseStr);
+ RestoreString(toc, PARALLEL_COPY_KEY_RANGE_TABLE, &rangeTableStr);
+
+ if (whereClauseStr)
+ {
+ Node *whereClauseCnvrtdFrmStr = (Node *) stringToNode(whereClauseStr);
+ cstate->whereClause = whereClauseCnvrtdFrmStr;
+ }
+
+ if (rangeTableStr)
+ {
+ List *rangeTableCnvrtdFrmStr = (List *) stringToNode(rangeTableStr);
+ cstate->range_table = rangeTableCnvrtdFrmStr;
+ }
+
+ /* Open and lock the relation, using the appropriate lock type. */
+ rel = table_open(shared_cstate->relid, RowExclusiveLock);
+ cstate->rel = rel;
+ InitializeParallelCopyInfo(shared_cstate, cstate, attlist);
+
+ CopyFrom(cstate);
+
+ if (rel != NULL)
+ table_close(rel, RowExclusiveLock);
+
+ /* Report WAL/buffer usage during parallel execution */
+ bufferusage = shm_toc_lookup(toc, PARALLEL_COPY_BUFFER_USAGE, false);
+ walusage = shm_toc_lookup(toc, PARALLEL_COPY_WAL_USAGE, false);
+ InstrEndParallelQuery(&bufferusage[ParallelWorkerNumber],
+ &walusage[ParallelWorkerNumber]);
+
+ MemoryContextSwitchTo(oldcontext);
+ pfree(cstate);
+ return;
+}
+
+/*
+ * ParallelCopyFrom - parallel copy leader's functionality.
+ *
+ * Leader executes the before statement for before statement trigger, if before
+ * statement trigger is present. It will read the table data from the file and
+ * copy the contents to DSM data blocks. It will then read the input contents
+ * from the DSM data block and identify the records based on line breaks. This
+ * information is called line or a record that need to be inserted into a
+ * relation. The line information will be stored in ParallelCopyLineBoundary DSM
+ * data structure. Workers will then process this information and insert the
+ * data in to table. It will repeat this process until the all data is read from
+ * the file and all the DSM data blocks are processed. While processing if
+ * leader identifies that DSM Data blocks or DSM ParallelCopyLineBoundary data
+ * structures is full, leader will wait till the worker frees up some entries
+ * and repeat the process. It will wait till all the lines populated are
+ * processed by the workers and exits.
+ */
+static void
+ParallelCopyFrom(CopyState cstate)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+
+ pcshared_info->is_read_in_progress = false;
+ cstate->cur_lineno = 0;
+}
+
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -1141,6 +1822,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
if (is_from)
{
+ ParallelContext *pcxt = NULL;
Assert(rel);
/* check read-only transaction and parallel mode */
@@ -1150,7 +1832,24 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);
cstate->whereClause = whereClause;
- *processed = CopyFrom(cstate); /* copy from file to database */
+ cstate->is_parallel = false;
+
+ if (cstate->nworkers > 0)
+ pcxt = BeginParallelCopy(cstate->nworkers, cstate, stmt->attlist,
+ relid);
+
+ if (pcxt)
+ {
+ ParallelCopyFrom(cstate);
+
+ /* Wait for all copy workers to finish */
+ WaitForParallelWorkersToFinish(pcxt);
+ *processed = pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+ EndParallelCopy(pcxt);
+ }
+ else
+ *processed = CopyFrom(cstate); /* copy from file to database */
+
EndCopyFrom(cstate);
}
else
@@ -1199,6 +1898,7 @@ ProcessCopyOptions(ParseState *pstate,
cstate->is_copy_from = is_from;
cstate->file_encoding = -1;
+ cstate->nworkers = -1;
/* Extract options from the statement node tree */
foreach(option, options)
@@ -1367,6 +2067,53 @@ ProcessCopyOptions(ParseState *pstate,
defel->defname),
parser_errposition(pstate, defel->location)));
}
+ else if (strcmp(defel->defname, "parallel") == 0)
+ {
+ char *endptr, *str;
+ long val;
+
+ if (!cstate->is_copy_from)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("parallel option supported only for copy from"),
+ parser_errposition(pstate, defel->location)));
+ if (cstate->nworkers >= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options"),
+ parser_errposition(pstate, defel->location)));
+
+ str = defGetString(defel);
+
+ errno = 0; /* To distinguish success/failure after call */
+ val = strtol(str, &endptr, 10);
+
+ /* Check for various possible errors */
+ if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+ || (errno != 0 && val == 0) ||
+ *endptr)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("improper use of argument to option \"%s\"",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
+
+ if (endptr == str)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("no digits were found in argument to option \"%s\"",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
+
+ cstate->nworkers = (int) val;
+
+ if (cstate->nworkers <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("argument to option \"%s\" must be a positive integer greater than zero",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
+ }
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1720,11 +2467,12 @@ BeginCopy(ParseState *pstate,
/*
* PopulateCommonCstateInfo - Populates the common variables required for copy
- * from operation. This is a helper function for BeginCopy function.
+ * from operation. This is a helper function for BeginCopy &
+ * InitializeParallelCopyInfo function.
*/
static void
PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc,
- List *attnamelist)
+ List *attnamelist)
{
int num_phys_attrs;
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index c639833..82843c6 100644
--- a/src/include/commands/copy.h
+++ b/src/include/commands/copy.h
@@ -14,6 +14,7 @@
#ifndef COPY_H
#define COPY_H
+#include "access/parallel.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
#include "parser/parse_node.h"
@@ -41,4 +42,5 @@ extern uint64 CopyFrom(CopyState cstate);
extern DestReceiver *CreateCopyDestReceiver(void);
+extern void ParallelCopyMain(dsm_segment *seg, shm_toc *toc);
#endif /* COPY_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 7eaaad1..3a83d0f 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1699,6 +1699,12 @@ ParallelBitmapHeapState
ParallelBlockTableScanDesc
ParallelCompletionPtr
ParallelContext
+ParallelCopyLineBoundaries
+ParallelCopyLineBoundary
+ParallelCopyData
+ParallelCopyDataBlock
+ParallelCopyLineBuf
+ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
ParallelHashJoinBatch
@@ -2215,6 +2221,7 @@ SerCommitSeqNo
SerialControl
SerializableXactHandle
SerializedActiveRelMaps
+SerializedParallelCopyState
SerializedReindexState
SerializedSnapshotData
SerializedTransactionState
--
1.8.3.1
[application/x-patch] v2-0003-Allow-copy-from-command-to-process-data-from-file.patch (43.0K, ../../CALj2ACUKKWnju+Xbm1tOtDQj=FxSVBZ1acZcF77RWuN6MCDHxg@mail.gmail.com/4-v2-0003-Allow-copy-from-command-to-process-data-from-file.patch)
download | inline diff:
From f3ec188c3c1e11fdbb716ec12862a891a95b396f Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:31:42 +0530
Subject: [PATCH v2 3/5] Allow copy from command to process data from
file/STDIN contents to a table in parallel.
This feature allows the copy from to leverage multiple CPUs in order to copy
data from file/STDIN to a table. This adds a PARALLEL option to COPY FROM
command where the user can specify the number of workers that can be used
to perform the COPY FROM command. Specifying zero as number of workers will
disable parallelism.
The backend, to which the "COPY FROM" query is submitted acts as leader with
the responsibility of reading data from the file/stdin, launching at most n
number of workers as specified with PARALLEL 'n' option in the "COPY FROM"
query. The leader populates the common data required for the workers execution
in the DSM and shares it with the workers. The leader then executes before
statement triggers if there exists any. Leader populates DSM lines which
includes the start offset and line size, while populating the lines it reads
as many blocks as required into the DSM data blocks from the file. Each block
is of 64K size. The leader parses the data to identify a line, the existing
logic from CopyReadLineText which identifies the lines with some changes was
used for this. Leader checks if a free line is available to copy the
information, if there is no free line it waits till the required line is
freed up by the worker and then copies the identified lines information
(offset & line size) into the DSM lines. This process is repeated till the
complete file is processed. Simultaneously, the workers cache the lines(50)
locally into the local memory and release the lines to the leader for further
populating. Each worker processes the lines that it cached and inserts it into
the table. The leader does not participate in the insertion of data, leaders
only responsibility will be to identify the lines as fast as possible for the
workers to do the actual copy operation. The leader waits till all the lines
populated are processed by the workers and exits.
We have chosen this design based on the reason "that everything stalls if the
leader doesn't accept further input data, as well as when there are no
available splitted chunks so it doesn't seem like a good idea to have the
leader do other work. This is backed by the performance data where we have
seen that with 1 worker there is just a 5-10% performance difference".
---
src/backend/access/common/toast_internals.c | 11 +-
src/backend/access/heap/heapam.c | 13 -
src/backend/access/transam/xact.c | 31 +
src/backend/commands/copy.c | 890 ++++++++++++++++++++++++++--
src/bin/psql/tab-complete.c | 2 +-
src/include/access/xact.h | 2 +
src/tools/pgindent/typedefs.list | 1 +
7 files changed, 896 insertions(+), 54 deletions(-)
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 25a81e5..586d53d 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -17,6 +17,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heaptoast.h"
+#include "access/parallel.h"
#include "access/table.h"
#include "access/toast_internals.h"
#include "access/xact.h"
@@ -116,7 +117,15 @@ toast_save_datum(Relation rel, Datum value,
TupleDesc toasttupDesc;
Datum t_values[3];
bool t_isnull[3];
- CommandId mycid = GetCurrentCommandId(true);
+
+ /*
+ * Parallel copy can insert toast tuples, in case of parallel copy the
+ * command would have been set already by calling AssignCommandIdForWorker.
+ * For parallel copy call GetCurrentCommandId to get currentCommandId by
+ * passing used as false, as this is taken care earlier.
+ */
+ CommandId mycid = IsParallelWorker() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
struct varlena *result;
struct varatt_external toast_pointer;
union
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 5eef225..bd7a7fc 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2023,19 +2023,6 @@ static HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
- /*
- * Parallel operations are required to be strictly read-only in a parallel
- * worker. Parallel inserts are not safe even in the leader in the
- * general case, because group locking means that heavyweight locks for
- * relation extension or GIN page locks will not conflict between members
- * of a lock group, but we don't prohibit that case here because there are
- * useful special cases that we can safely allow, such as CREATE TABLE AS.
- */
- if (IsParallelWorker())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot insert tuples in a parallel worker")));
-
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index d4f7c29..9bff390 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -504,6 +504,37 @@ GetCurrentFullTransactionIdIfAny(void)
}
/*
+ * AssignFullTransactionIdForWorker
+ *
+ * For parallel copy, transaction id of leader will be used by the workers.
+ */
+void
+AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId)
+{
+ TransactionState s = CurrentTransactionState;
+
+ Assert((IsInParallelMode() || IsParallelWorker()));
+ s->fullTransactionId = fullTransactionId;
+}
+
+/*
+ * AssignCommandIdForWorker
+ *
+ * For parallel copy, command id of leader will be used by the workers.
+ */
+void
+AssignCommandIdForWorker(CommandId commandId, bool used)
+{
+ Assert((IsInParallelMode() || IsParallelWorker()));
+
+ /* this is global to a transaction, not subtransaction-local */
+ if (used)
+ currentCommandIdUsed = true;
+
+ currentCommandId = commandId;
+}
+
+/*
* MarkCurrentTransactionIdLoggedIfAny
*
* Remember that the current xid - if it is assigned - now has been wal logged.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3dcdfc1..c65fc98 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -26,6 +26,7 @@
#include "access/xlog.h"
#include "catalog/dependency.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_proc_d.h"
#include "catalog/pg_type.h"
#include "commands/copy.h"
#include "commands/defrem.h"
@@ -40,11 +41,13 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
+#include "pgstat.h"
#include "port/pg_bswap.h"
#include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
@@ -95,6 +98,18 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+/*
+ * State of the line.
+ */
+typedef enum ParallelCopyLineState
+{
+ LINE_INIT, /* initial state of line */
+ LINE_LEADER_POPULATING, /* leader processing line */
+ LINE_LEADER_POPULATED, /* leader completed populating line */
+ LINE_WORKER_PROCESSING, /* worker processing line */
+ LINE_WORKER_PROCESSED /* worker completed processing line */
+}ParallelCopyLineState;
+
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
/*
@@ -123,6 +138,7 @@ typedef enum CopyInsertMethod
#define IsParallelCopy() (cstate->is_parallel)
#define IsLeader() (cstate->pcdata->is_leader)
+#define IsWorker() (IsParallelCopy() && !IsLeader())
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
@@ -556,9 +572,13 @@ if (1) \
{ \
if (raw_buf_ptr > cstate->raw_buf_index) \
{ \
- appendBinaryStringInfo(&cstate->line_buf, \
- cstate->raw_buf + cstate->raw_buf_index, \
- raw_buf_ptr - cstate->raw_buf_index); \
+ if (!IsParallelCopy()) \
+ appendBinaryStringInfo(&cstate->line_buf, \
+ cstate->raw_buf + cstate->raw_buf_index, \
+ raw_buf_ptr - cstate->raw_buf_index); \
+ else \
+ line_size += raw_buf_ptr - cstate->raw_buf_index; \
+ \
cstate->raw_buf_index = raw_buf_ptr; \
} \
} else ((void) 0)
@@ -571,26 +591,65 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/* Begin parallel copy Macros */
+#define SET_NEWLINE_SIZE() \
+{ \
+ if (cstate->eol_type == EOL_NL || cstate->eol_type == EOL_CR) \
+ new_line_size = 1; \
+ else if (cstate->eol_type == EOL_CRNL) \
+ new_line_size = 2; \
+ else \
+ new_line_size = 0; \
+}
+
+/*
+ * COPY_WAIT_TO_PROCESS - Wait before continuing to process.
+ */
+#define COPY_WAIT_TO_PROCESS() \
+{ \
+ CHECK_FOR_INTERRUPTS(); \
+ (void) WaitLatch(MyLatch, \
+ WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, \
+ 1L, WAIT_EVENT_PG_SLEEP); \
+ ResetLatch(MyLatch); \
+}
+
/*
* CLEAR_EOL_LINE - Wrapper for clearing EOL.
*/
#define CLEAR_EOL_LINE() \
if (!result && !IsHeaderLine()) \
- ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
- cstate->line_buf.len, \
- &cstate->line_buf.len) \
+{ \
+ if (IsParallelCopy()) \
+ ClearEOLFromCopiedData(cstate, cstate->raw_buf, \
+ raw_buf_ptr, &line_size); \
+ else \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len); \
+} \
/*
* INCREMENTPROCESSED - Increment the lines processed.
*/
-#define INCREMENTPROCESSED(processed) \
-processed++;
+#define INCREMENTPROCESSED(processed) \
+{ \
+ if (!IsParallelCopy()) \
+ processed++; \
+ else \
+ pg_atomic_add_fetch_u64(&cstate->pcdata->pcshared_info->processed, 1); \
+}
/*
* GETPROCESSED - Get the lines processed.
*/
#define GETPROCESSED(processed) \
-return processed;
+if (!IsParallelCopy()) \
+ return processed; \
+else \
+ return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+
+/* End parallel copy Macros */
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -645,7 +704,10 @@ static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
-
+static void ExecBeforeStmtTrigger(CopyState cstate);
+static void CheckTargetRelValidity(CopyState cstate);
+static void PopulateCstateCatalogInfo(CopyState cstate);
+static pg_attribute_always_inline uint32 GetLinePosition(CopyState cstate);
/*
* SerializeParallelCopyState - Copy shared_cstate using cstate information.
*/
@@ -727,6 +789,137 @@ PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
}
/*
+ * IsTriggerFunctionParallelSafe - Check if the trigger function is parallel
+ * safe for the triggers. Return false if any one of the trigger has parallel
+ * unsafe function.
+ */
+static pg_attribute_always_inline bool
+IsTriggerFunctionParallelSafe(TriggerDesc *trigdesc)
+{
+ int i;
+ for (i = 0; i < trigdesc->numtriggers; i++)
+ {
+ Trigger *trigger = &trigdesc->triggers[i];
+ int trigtype = RI_TRIGGER_NONE;
+
+ if (func_parallel(trigger->tgfoid) != PROPARALLEL_SAFE)
+ return false;
+
+ /* If the trigger is parallel safe, also look for RI_TRIGGER. */
+ trigtype = RI_FKey_trigger_type(trigger->tgfoid);
+ if (trigtype == RI_TRIGGER_PK || trigtype == RI_TRIGGER_FK)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * CheckExprParallelSafety - determine parallel safety of volatile expressions
+ * in default clause of column definition or in where clause and return true if
+ * they are parallel safe.
+ */
+static pg_attribute_always_inline bool
+CheckExprParallelSafety(CopyState cstate)
+{
+ if (contain_volatile_functions(cstate->whereClause))
+ {
+ if (max_parallel_hazard((Query *)cstate->whereClause) != PROPARALLEL_SAFE)
+ return false;
+ }
+
+ /*
+ * Check if any of the column has volatile default expression. if yes, and
+ * they are not parallel safe, then parallelism is not allowed. For
+ * instance, if there are any serial/bigserial columns for which nextval()
+ * default expression which is parallel unsafe is associated, parallelism
+ * should not be allowed. In non parallel copy volatile functions are not
+ * checked for nextval().
+ */
+ if (cstate->defexprs != NULL && cstate->num_defaults != 0)
+ {
+ int i;
+ for (i = 0; i < cstate->num_defaults; i++)
+ {
+ bool volatile_expr = contain_volatile_functions((Node *)cstate->defexprs[i]->expr);
+ if (volatile_expr &&
+ (max_parallel_hazard((Query *)cstate->defexprs[i]->expr)) !=
+ PROPARALLEL_SAFE)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * FindInsertMethod - determine insert mode single, multi, or multi conditional.
+ */
+static pg_attribute_always_inline CopyInsertMethod
+FindInsertMethod(CopyState cstate)
+{
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_before_row ||
+ cstate->rel->trigdesc->trig_insert_instead_row))
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+ cstate->rel->trigdesc != NULL &&
+ cstate->rel->trigdesc->trig_insert_new_table)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ return CIM_MULTI_CONDITIONAL;
+
+ return CIM_MULTI;
+}
+
+/*
+ * IsParallelCopyAllowed - check for the cases where parallel copy is not
+ * applicable.
+ */
+static pg_attribute_always_inline bool
+IsParallelCopyAllowed(CopyState cstate)
+{
+ /* Parallel copy not allowed for frontend (2.0 protocol) & binary option. */
+ if ((cstate->copy_dest == COPY_OLD_FE) || cstate->binary)
+ return false;
+
+ /* Check if copy is into foreign table or temporary table. */
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
+ RelationUsesLocalBuffers(cstate->rel))
+ return false;
+
+ /* Check if trigger function is parallel safe. */
+ if (cstate->rel->trigdesc != NULL &&
+ !IsTriggerFunctionParallelSafe(cstate->rel->trigdesc))
+ return false;
+
+ /*
+ * Check if there is after statement or instead of trigger or transition
+ * table triggers.
+ */
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_after_statement ||
+ cstate->rel->trigdesc->trig_insert_instead_row ||
+ cstate->rel->trigdesc->trig_insert_new_table))
+ return false;
+
+ /* Check if the volatile expressions are parallel safe, if present any. */
+ if (!CheckExprParallelSafety(cstate))
+ return false;
+
+ /* Check if the insertion mode is single. */
+ if (FindInsertMethod(cstate) == CIM_SINGLE)
+ return false;
+
+ return true;
+}
+
+/*
* BeginParallelCopy - start parallel copy tasks.
*
* Get the number of workers required to perform the parallel copy. The data
@@ -754,6 +947,7 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
ParallelCopyData *pcdata;
MemoryContext oldcontext;
+ CheckTargetRelValidity(cstate);
parallel_workers = Min(nworkers, max_worker_processes);
/* Can't perform copy in parallel */
@@ -765,6 +959,15 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
MemoryContextSwitchTo(oldcontext);
cstate->pcdata = pcdata;
+ /*
+ * User chosen parallel copy. Determine if the parallel copy is actually
+ * allowed. If not, go with the non-parallel mode.
+ */
+ if (!IsParallelCopyAllowed(cstate))
+ return NULL;
+
+ full_transaction_id = GetCurrentFullTransactionId();
+
EnterParallelMode();
pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
parallel_workers);
@@ -973,9 +1176,212 @@ InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
cstate->line_buf_converted = false;
cstate->raw_buf = NULL;
cstate->raw_buf_index = cstate->raw_buf_len = 0;
+
+ PopulateCstateCatalogInfo(cstate);
+
+ /* Create workspace for CopyReadAttributes results. */
+ if (!cstate->binary)
+ {
+ AttrNumber attr_count = list_length(cstate->attnumlist);
+
+ cstate->max_fields = attr_count;
+ cstate->raw_fields = (char **)palloc(attr_count * sizeof(char *));
+ }
+}
+
+/*
+ * CacheLineInfo - Cache the line information to local memory.
+ */
+static bool
+CacheLineInfo(CopyState cstate, uint32 buff_count)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ uint32 write_pos;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 offset;
+ int dataSize;
+ int copiedSize = 0;
+
+ resetStringInfo(&pcdata->worker_line_buf[buff_count].line_buf);
+ write_pos = GetLinePosition(cstate);
+ if (-1 == write_pos)
+ return true;
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ if (pg_atomic_read_u32(&lineInfo->line_size) == 0)
+ goto empty_data_line_update;
+
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ /* Get the offset information from where the data must be copied. */
+ offset = lineInfo->start_offset;
+ pcdata->worker_line_buf[buff_count].cur_lineno = lineInfo->cur_lineno;
+
+ elog(DEBUG1, "[Worker] Processing - line position:%d, block:%d, unprocessed lines:%d, offset:%d, line size:%d",
+ write_pos, lineInfo->first_block,
+ pg_atomic_read_u32(&data_blk_ptr->unprocessed_line_parts),
+ offset, pg_atomic_read_u32(&lineInfo->line_size));
+
+ for (;;)
+ {
+ uint8 skip_bytes = data_blk_ptr->skip_bytes;
+ /*
+ * There is a possibility that the above loop has come out because
+ * data_blk_ptr->curr_blk_completed is set, but dataSize read might
+ * be an old value, if data_blk_ptr->curr_blk_completed and the line is
+ * completed, line_size will be set. Read the line_size again to be
+ * sure if it is complete or partial block.
+ */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+ if (dataSize)
+ {
+ int remainingSize = dataSize - copiedSize;
+ if (!remainingSize)
+ break;
+
+ /* Whole line is in current block. */
+ if (remainingSize + offset + skip_bytes < DATA_BLOCK_SIZE)
+ {
+ appendBinaryStringInfo(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ remainingSize);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts,
+ 1);
+ break;
+ }
+ else
+ {
+ /* Line is spread across the blocks. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+ while (copiedSize < dataSize)
+ {
+ uint32 currentBlockCopySize;
+ ParallelCopyDataBlock *currBlkPtr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ skip_bytes = currBlkPtr->skip_bytes;
+
+ /*
+ * If complete data is present in current block use
+ * dataSize - copiedSize, or copy the whole block from
+ * current block.
+ */
+ currentBlockCopySize = Min(dataSize - copiedSize, DATA_BLOCK_SIZE - skip_bytes);
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &currBlkPtr->data[0],
+ currentBlockCopySize);
+ pg_atomic_sub_fetch_u32(&currBlkPtr->unprocessed_line_parts, 1);
+ copiedSize += currentBlockCopySize;
+ data_blk_ptr = currBlkPtr;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ /* Copy this complete block from the current offset. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+
+ /*
+ * Reset the offset. For the first copy, copy from the offset. For
+ * the subsequent copy the complete block.
+ */
+ offset = 0;
+
+ /* Set data_blk_ptr to the following block. */
+ data_blk_ptr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ }
+
+ for (;;)
+ {
+ /* Get the size of this line */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ /*
+ * If the data is present in current block lineInfo.line_size
+ * will be updated. If the data is spread across the blocks either
+ * of lineInfo.line_size or data_blk_ptr->curr_blk_completed can
+ * be updated. lineInfo.line_size will be updated if the complete
+ * read is finished. data_blk_ptr->curr_blk_completed will be
+ * updated if processing of current block is finished and data
+ * processing is not finished.
+ */
+ if (data_blk_ptr->curr_blk_completed || (dataSize != -1))
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+ }
+
+empty_data_line_update:
+ elog(DEBUG1, "[Worker] Completed processing line:%d", write_pos);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_WORKER_PROCESSED);
+ pg_atomic_write_u32(&lineInfo->line_size, -1);
+ pg_atomic_add_fetch_u64(&pcshared_info->total_worker_processed, 1);
+ return false;
}
/*
+ * GetWorkerLine - Returns a line for worker to process.
+ */
+static bool
+GetWorkerLine(CopyState cstate)
+{
+ uint32 buff_count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+
+ /*
+ * Copy the line data to line_buf and release the line position so that the
+ * worker can continue loading data.
+ */
+ if (pcdata->worker_line_buf_pos < pcdata->worker_line_buf_count)
+ goto return_line;
+
+ pcdata->worker_line_buf_pos = 0;
+ pcdata->worker_line_buf_count = 0;
+
+ for (buff_count = 0; buff_count < WORKER_CHUNK_COUNT; buff_count++)
+ {
+ bool result = CacheLineInfo(cstate, buff_count);
+ if (result)
+ break;
+
+ pcdata->worker_line_buf_count++;
+ }
+
+ if (pcdata->worker_line_buf_count)
+ goto return_line;
+ else
+ resetStringInfo(&cstate->line_buf);
+
+ return true;
+
+return_line:
+ cstate->line_buf = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].line_buf;
+ cstate->cur_lineno = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].cur_lineno;
+ cstate->line_buf_valid = true;
+
+ /* Mark that encoding conversion hasn't occurred yet. */
+ cstate->line_buf_converted = false;
+ ConvertToServerEncoding(cstate);
+ pcdata->worker_line_buf_pos++;
+ return false;
+ }
+
+/*
* ParallelCopyMain - parallel copy worker's code.
*
* Where clause handling, convert tuple to columns, add default null values for
@@ -1024,6 +1430,8 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
pcdata->pcshared_info = pcshared_info;
+ AssignFullTransactionIdForWorker(pcshared_info->full_transaction_id);
+ AssignCommandIdForWorker(pcshared_info->mycid, true);
shared_cstate = (SerializedParallelCopyState *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
cstate->null_print = (char *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
@@ -1084,6 +1492,33 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
}
/*
+ * UpdateBlockInLineInfo - Update the line information.
+ */
+static pg_attribute_always_inline int
+UpdateBlockInLineInfo(CopyState cstate, uint32 blk_pos,
+ uint32 offset, uint32 line_size, uint32 line_state)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ ParallelCopyLineBoundary *lineInfo;
+ int line_pos = lineBoundaryPtr->pos;
+
+ /* Update the line information for the worker to pick and process. */
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ while (pg_atomic_read_u32(&lineInfo->line_size) != -1)
+ COPY_WAIT_TO_PROCESS()
+
+ lineInfo->first_block = blk_pos;
+ lineInfo->start_offset = offset;
+ lineInfo->cur_lineno = cstate->cur_lineno;
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, line_state);
+ lineBoundaryPtr->pos = (lineBoundaryPtr->pos + 1) % RINGSIZE;
+
+ return line_pos;
+}
+
+/*
* ParallelCopyFrom - parallel copy leader's functionality.
*
* Leader executes the before statement for before statement trigger, if before
@@ -1106,8 +1541,298 @@ ParallelCopyFrom(CopyState cstate)
ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+ /* Execute the before statement triggers from the leader */
+ ExecBeforeStmtTrigger(cstate);
+
+ /* On input just throw the header line away. */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ {
+ pcshared_info->is_read_in_progress = false;
+ return; /* done */
+ }
+ }
+
+ for (;;)
+ {
+ bool done;
+ cstate->cur_lineno++;
+
+ /* Actually read the line into memory here. */
+ done = CopyReadLine(cstate);
+
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ break;
+ }
+
pcshared_info->is_read_in_progress = false;
cstate->cur_lineno = 0;
+ }
+
+/*
+ * GetLinePosition - return the line position that worker should process.
+ */
+static uint32
+GetLinePosition(CopyState cstate)
+{
+ ParallelCopyData *pcdata = cstate->pcdata;
+ ParallelCopyShmInfo *pcshared_info = pcdata->pcshared_info;
+ uint32 previous_pos = pcdata->worker_processed_pos;
+ uint32 write_pos = (previous_pos == -1) ? 0 : (previous_pos + 1) % RINGSIZE;
+ for (;;)
+ {
+ int dataSize;
+ bool is_read_in_progress = pcshared_info->is_read_in_progress;
+ ParallelCopyLineBoundary *lineInfo;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineState line_state = LINE_LEADER_POPULATED;
+ ParallelCopyLineState curr_line_state;
+ CHECK_FOR_INTERRUPTS();
+
+ /* File read completed & no elements to process. */
+ if (!is_read_in_progress &&
+ (pcshared_info->populated ==
+ pg_atomic_read_u64(&pcshared_info->total_worker_processed)))
+ {
+ write_pos = -1;
+ break;
+ }
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ curr_line_state = pg_atomic_read_u32(&lineInfo->line_state);
+ if ((write_pos % WORKER_CHUNK_COUNT == 0) &&
+ (curr_line_state == LINE_WORKER_PROCESSED ||
+ curr_line_state == LINE_WORKER_PROCESSING))
+ {
+ pcdata->worker_processed_pos = write_pos;
+ write_pos = (write_pos + WORKER_CHUNK_COUNT) % RINGSIZE;
+ continue;
+ }
+
+ /* Get the size of this line. */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ if (dataSize != 0) /* If not an empty line. */
+ {
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ if (!data_blk_ptr->curr_blk_completed && (dataSize == -1))
+ {
+ /* Wait till the current line or block is added. */
+ COPY_WAIT_TO_PROCESS()
+ continue;
+ }
+ }
+
+ /* Make sure that no worker has consumed this element. */
+ if (pg_atomic_compare_exchange_u32(&lineInfo->line_state,
+ &line_state, LINE_WORKER_PROCESSING))
+ break;
+ }
+
+ pcdata->worker_processed_pos = write_pos;
+ return write_pos;
+}
+
+/*
+ * GetFreeCopyBlock - Get a free block for data to be copied.
+ */
+static pg_attribute_always_inline uint32
+GetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ int count = 0;
+ uint32 last_free_block = pcshared_info->cur_block_pos;
+ uint32 block_pos = (last_free_block != -1) ? ((last_free_block + 1) % MAX_BLOCKS_COUNT): 0;
+
+ /*
+ * Get a new block for copying data, don't check current block, current
+ * block will have some unprocessed data.
+ */
+ while (count < (MAX_BLOCKS_COUNT - 1))
+ {
+ ParallelCopyDataBlock *dataBlkPtr = &pcshared_info->data_blocks[block_pos];
+ uint32 unprocessed_line_parts = pg_atomic_read_u32(&dataBlkPtr->unprocessed_line_parts);
+ if (unprocessed_line_parts == 0)
+ {
+ dataBlkPtr->curr_blk_completed = false;
+ dataBlkPtr->skip_bytes = 0;
+ pcshared_info->cur_block_pos = block_pos;
+ return block_pos;
+ }
+
+ block_pos = (block_pos + 1) % MAX_BLOCKS_COUNT;
+ count++;
+ }
+
+ return -1;
+}
+
+/*
+ * WaitGetFreeCopyBlock - If there are no blocks available, wait and get a block
+ * for copying data.
+ */
+static uint32
+WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ uint32 new_free_pos = -1;
+ for (;;)
+ {
+ new_free_pos = GetFreeCopyBlock(pcshared_info);
+ if (new_free_pos != -1) /* We have got one block, break now. */
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+
+ return new_free_pos;
+}
+
+/*
+ * SetRawBufForLoad - Set raw_buf to the shared memory where the file data must
+ * be read.
+ */
+static void
+SetRawBufForLoad(CopyState cstate, uint32 line_size, uint32 copy_buf_len,
+ uint32 raw_buf_ptr, char **copy_raw_buf)
+{
+ ParallelCopyShmInfo *pcshared_info;
+ uint32 cur_block_pos;
+ uint32 next_block_pos;
+ ParallelCopyDataBlock *cur_data_blk_ptr = NULL;
+ ParallelCopyDataBlock *next_data_blk_ptr = NULL;
+
+ if (!IsParallelCopy())
+ return;
+
+ pcshared_info = cstate->pcdata->pcshared_info;
+ cur_block_pos = pcshared_info->cur_block_pos;
+ cur_data_blk_ptr = (cstate->raw_buf) ? &pcshared_info->data_blocks[cur_block_pos] : NULL;
+ next_block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ next_data_blk_ptr = &pcshared_info->data_blocks[next_block_pos];
+
+ /* set raw_buf to the data block in shared memory */
+ cstate->raw_buf = next_data_blk_ptr->data;
+ *copy_raw_buf = cstate->raw_buf;
+ if (cur_data_blk_ptr && line_size)
+ {
+ /*
+ * Mark the previous block as completed, worker can start copying this
+ * data.
+ */
+ cur_data_blk_ptr->following_block = next_block_pos;
+ pg_atomic_add_fetch_u32(&cur_data_blk_ptr->unprocessed_line_parts, 1);
+ cur_data_blk_ptr->skip_bytes = copy_buf_len - raw_buf_ptr;
+ cur_data_blk_ptr->curr_blk_completed = true;
+ }
+}
+
+/*
+ * EndLineParallelCopy - Update the line information in shared memory.
+ */
+static void
+EndLineParallelCopy(CopyState cstate, uint32 line_pos, uint32 line_size,
+ uint32 raw_buf_ptr)
+{
+ uint8 new_line_size;
+ if (!IsParallelCopy())
+ return;
+
+ if (!IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ SET_NEWLINE_SIZE()
+ if (line_size)
+ {
+ ParallelCopyLineBoundary *lineInfo = &lineBoundaryPtr->ring[line_pos];
+ /*
+ * If the new_line_size > raw_buf_ptr, then the new block has only
+ * new line char content. The unprocessed count should not be
+ * increased in this case.
+ */
+ if (raw_buf_ptr > new_line_size)
+ {
+ uint32 cur_block_pos = pcshared_info->cur_block_pos;
+ ParallelCopyDataBlock *curr_data_blk_ptr = &pcshared_info->data_blocks[cur_block_pos];
+ pg_atomic_add_fetch_u32(&curr_data_blk_ptr->unprocessed_line_parts, 1);
+ }
+
+ /* Update line size. */
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_LEADER_POPULATED);
+ elog(DEBUG1, "[Leader] After adding - line position:%d, line_size:%d",
+ line_pos, line_size);
+ pcshared_info->populated++;
+ }
+ else if (new_line_size)
+ {
+ /* This means only new line char, empty record should be inserted.*/
+ ParallelCopyLineBoundary *lineInfo;
+ line_pos = UpdateBlockInLineInfo(cstate, -1, -1, 0,
+ LINE_LEADER_POPULATED);
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ elog(DEBUG1, "[Leader] Added empty line with offset:%d, line position:%d, line size:%d",
+ lineInfo->start_offset, line_pos,
+ pg_atomic_read_u32(&lineInfo->line_size));
+ pcshared_info->populated++;
+ }
+ }
+}
+
+/*
+ * ExecBeforeStmtTrigger - Execute the before statement trigger, this will be
+ * executed for parallel copy by the leader process.
+ */
+static void
+ExecBeforeStmtTrigger(CopyState cstate)
+{
+ EState *estate = CreateExecutorState();
+ ResultRelInfo *resultRelInfo;
+
+ Assert(IsLeader());
+
+ /*
+ * We need a ResultRelInfo so we can use the regular executor's
+ * index-entry-making machinery. (There used to be a huge amount of code
+ * here that basically duplicated execUtils.c ...)
+ */
+ resultRelInfo = makeNode(ResultRelInfo);
+ InitResultRelInfo(resultRelInfo,
+ cstate->rel,
+ 1, /* must match rel's position in range_table */
+ NULL,
+ 0);
+
+ /* Verify the named relation is a valid target for INSERT */
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
+
+ estate->es_result_relations = resultRelInfo;
+ estate->es_num_result_relations = 1;
+ estate->es_result_relation_info = resultRelInfo;
+
+ ExecInitRangeTable(estate, cstate->range_table);
+
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+
+ /* Close any trigger target relations */
+ ExecCleanUpTriggerState(estate);
+
+ FreeExecutorState(estate);
}
/*
@@ -3584,7 +4309,8 @@ CopyFrom(CopyState cstate)
PartitionTupleRouting *proute = NULL;
ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
+ CommandId mycid = IsParallelCopy() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
int ti_options = 0; /* start with default options for insert */
BulkInsertState bistate = NULL;
CopyInsertMethod insertMethod;
@@ -3594,7 +4320,14 @@ CopyFrom(CopyState cstate)
bool has_instead_insert_row_trig;
bool leafpart_use_multi_insert = false;
- CheckTargetRelValidity(cstate);
+ /*
+ * Perform this check if it is not parallel copy. In case of parallel
+ * copy, this check is done by the leader, so that if any invalid case
+ * exist the copy from command will error out from the leader itself,
+ * avoiding launching workers, just to throw error.
+ */
+ if (!IsParallelCopy())
+ CheckTargetRelValidity(cstate);
/*
* If the target file is new-in-transaction, we assume that checking FSM
@@ -3634,7 +4367,8 @@ CopyFrom(CopyState cstate)
target_resultRelInfo = resultRelInfo;
/* Verify the named relation is a valid target for INSERT */
- CheckValidResultRel(resultRelInfo, CMD_INSERT);
+ if (!IsParallelCopy())
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
ExecOpenIndices(resultRelInfo, false);
@@ -3783,13 +4517,16 @@ CopyFrom(CopyState cstate)
has_instead_insert_row_trig = (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_instead_row);
- /*
- * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
- * should do this for COPY, since it's not really an "INSERT" statement as
- * such. However, executing these triggers maintains consistency with the
- * EACH ROW triggers that we already fire on COPY.
- */
- ExecBSInsertTriggers(estate, resultRelInfo);
+ if (!IsParallelCopy())
+ {
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+ }
econtext = GetPerTupleExprContext(estate);
@@ -3889,6 +4626,16 @@ CopyFrom(CopyState cstate)
!has_instead_insert_row_trig &&
resultRelInfo->ri_FdwRoutine == NULL;
+ /*
+ * If the table has any partitions that are either foreign or
+ * has BEFORE/INSTEAD OF triggers, we can't perform copy
+ * operations with parallel workers.
+ */
+ if (!leafpart_use_multi_insert && IsParallelWorker())
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot perform PARALLEL COPY if partition has BEFORE/INSTEAD OF triggers, or if the partition is foreign partition"),
+ errhint("Try COPY without PARALLEL option")));
+
/* Set the multi-insert buffer to use for this partition. */
if (leafpart_use_multi_insert)
{
@@ -4305,7 +5052,7 @@ BeginCopyFrom(ParseState *pstate,
* only in text mode.
*/
initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf = (IsParallelCopy()) ? NULL : (char *) palloc(RAW_BUF_SIZE + 1);
cstate->raw_buf_index = cstate->raw_buf_len = 0;
if (!cstate->binary)
{
@@ -4454,26 +5201,35 @@ NextCopyFromRawFields(CopyState cstate, char ***fields, int *nfields)
/* only available for text or csv input */
Assert(!cstate->binary);
- /* on input just throw the header line away */
- if (cstate->cur_lineno == 0 && cstate->header_line)
+ if (IsParallelCopy())
{
- cstate->cur_lineno++;
- if (CopyReadLine(cstate))
- return false; /* done */
+ done = GetWorkerLine(cstate);
+ if (done && cstate->line_buf.len == 0)
+ return false;
}
+ else
+ {
+ /* on input just throw the header line away */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ return false; /* done */
+ }
- cstate->cur_lineno++;
+ cstate->cur_lineno++;
- /* Actually read the line into memory here */
- done = CopyReadLine(cstate);
+ /* Actually read the line into memory here */
+ done = CopyReadLine(cstate);
- /*
- * EOF at start of line means we're done. If we see EOF after some
- * characters, we act as though it was newline followed by EOF, ie,
- * process the line and then exit loop on next iteration.
- */
- if (done && cstate->line_buf.len == 0)
- return false;
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ return false;
+ }
/* Parse the line into de-escaped field values */
if (cstate->csv_mode)
@@ -4721,9 +5477,31 @@ CopyReadLine(CopyState cstate)
*/
if (cstate->copy_dest == COPY_NEW_FE)
{
+ bool bIsFirst = true;
do
{
- cstate->raw_buf_index = cstate->raw_buf_len;
+ if (!IsParallelCopy())
+ cstate->raw_buf_index = cstate->raw_buf_len;
+ else
+ {
+ if (cstate->raw_buf_index == RAW_BUF_SIZE)
+ {
+ /* Get a new block if it is the first time, From the
+ * subsequent time, reset the index and re-use the same
+ * block.
+ */
+ if (bIsFirst)
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ uint32 block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ cstate->raw_buf = pcshared_info->data_blocks[block_pos].data;
+ bIsFirst = false;
+ }
+
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ }
+ }
+
} while (CopyLoadRawBuf(cstate));
}
}
@@ -4778,7 +5556,7 @@ static void
ConvertToServerEncoding(CopyState cstate)
{
/* Done reading the line. Convert it to server encoding. */
- if (cstate->need_transcoding)
+ if (cstate->need_transcoding && (!IsParallelCopy() || IsWorker()))
{
char *cvt;
cvt = pg_any_to_server(cstate->line_buf.data,
@@ -4817,6 +5595,11 @@ CopyReadLineText(CopyState cstate)
char quotec = '\0';
char escapec = '\0';
+ /* For parallel copy */
+ int line_size = 0;
+ int line_pos = 0;
+
+ cstate->eol_type = EOL_UNKNOWN;
if (cstate->csv_mode)
{
quotec = cstate->quote[0];
@@ -4871,6 +5654,8 @@ CopyReadLineText(CopyState cstate)
if (raw_buf_ptr >= copy_buf_len || need_data)
{
REFILL_LINEBUF;
+ SetRawBufForLoad(cstate, line_size, copy_buf_len, raw_buf_ptr,
+ ©_raw_buf);
/*
* Try to read some more data. This will certainly reset
@@ -5095,9 +5880,15 @@ CopyReadLineText(CopyState cstate)
* discard the data and the \. sequence.
*/
if (prev_raw_ptr > cstate->raw_buf_index)
- appendBinaryStringInfo(&cstate->line_buf,
+ {
+ if (!IsParallelCopy())
+ appendBinaryStringInfo(&cstate->line_buf,
cstate->raw_buf + cstate->raw_buf_index,
prev_raw_ptr - cstate->raw_buf_index);
+ else
+ line_size += prev_raw_ptr - cstate->raw_buf_index;
+ }
+
cstate->raw_buf_index = raw_buf_ptr;
result = true; /* report EOF */
break;
@@ -5149,6 +5940,26 @@ not_end_of_copy:
IF_NEED_REFILL_AND_EOF_BREAK(mblen - 1);
raw_buf_ptr += mblen - 1;
}
+
+ /*
+ * Skip the header line. Update the line here, this cannot be done at
+ * the beginning, as there is a possibility that file contains empty
+ * lines.
+ */
+ if (IsParallelCopy() && first_char_in_line && !IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 line_first_block = pcshared_info->cur_block_pos;
+ line_pos = UpdateBlockInLineInfo(cstate,
+ line_first_block,
+ cstate->raw_buf_index, -1,
+ LINE_LEADER_POPULATING);
+ lineInfo = &pcshared_info->line_boundaries.ring[line_pos];
+ elog(DEBUG1, "[Leader] Adding - block:%d, offset:%d, line position:%d",
+ line_first_block, lineInfo->start_offset, line_pos);
+ }
+
first_char_in_line = false;
} /* end of outer loop */
@@ -5157,6 +5968,7 @@ not_end_of_copy:
*/
REFILL_LINEBUF;
CLEAR_EOL_LINE();
+ EndLineParallelCopy(cstate, line_pos, line_size, raw_buf_ptr);
return result;
}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index c4af40b..c95db78 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2353,7 +2353,7 @@ psql_completion(const char *text, int start, int end)
/* Complete COPY <sth> FROM|TO filename WITH ( */
else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "("))
COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
- "HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
+ "HEADER", "PARALLEL", "QUOTE", "ESCAPE", "FORCE_QUOTE",
"FORCE_NOT_NULL", "FORCE_NULL", "ENCODING");
/* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 5348011..4ea02f7 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -381,6 +381,8 @@ extern FullTransactionId GetTopFullTransactionId(void);
extern FullTransactionId GetTopFullTransactionIdIfAny(void);
extern FullTransactionId GetCurrentFullTransactionId(void);
extern FullTransactionId GetCurrentFullTransactionIdIfAny(void);
+extern void AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId);
+extern void AssignCommandIdForWorker(CommandId commandId, bool used);
extern void MarkCurrentTransactionIdLoggedIfAny(void);
extern bool SubTransactionIsActive(SubTransactionId subxid);
extern CommandId GetCurrentCommandId(bool used);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 3a83d0f..ba93f65 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1704,6 +1704,7 @@ ParallelCopyLineBoundary
ParallelCopyData
ParallelCopyDataBlock
ParallelCopyLineBuf
+ParallelCopyLineState
ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
--
1.8.3.1
[application/x-patch] v2-0004-Documentation-for-parallel-copy.patch (2.0K, ../../CALj2ACUKKWnju+Xbm1tOtDQj=FxSVBZ1acZcF77RWuN6MCDHxg@mail.gmail.com/5-v2-0004-Documentation-for-parallel-copy.patch)
download | inline diff:
From e543e3b3def9367bd30191ea334c26fb7e372b96 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:00:36 +0530
Subject: [PATCH v2 4/5] Documentation for parallel copy.
This patch has the documentation changes for parallel copy.
---
doc/src/sgml/ref/copy.sgml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index 18189ab..95d349d 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -37,6 +37,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
DELIMITER '<replaceable class="parameter">delimiter_character</replaceable>'
NULL '<replaceable class="parameter">null_string</replaceable>'
HEADER [ <replaceable class="parameter">boolean</replaceable> ]
+ PARALLEL <replaceable class="parameter">integer</replaceable>
QUOTE '<replaceable class="parameter">quote_character</replaceable>'
ESCAPE '<replaceable class="parameter">escape_character</replaceable>'
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
@@ -275,6 +276,21 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
</varlistentry>
<varlistentry>
+ <term><literal>PARALLEL</literal></term>
+ <listitem>
+ <para>
+ Perform <command>COPY FROM</command> in parallel using <replaceable
+ class="parameter"> integer</replaceable> background workers. Please
+ note that it is not guaranteed that the number of parallel workers
+ specified in <replaceable class="parameter">integer</replaceable> will
+ be used during execution. It is possible for a copy to run with fewer
+ workers than specified, or even with no workers at all. This option is
+ allowed only in <command>COPY FROM</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>QUOTE</literal></term>
<listitem>
<para>
--
1.8.3.1
[application/x-patch] v2-0005-Tests-for-parallel-copy.patch (19.8K, ../../CALj2ACUKKWnju+Xbm1tOtDQj=FxSVBZ1acZcF77RWuN6MCDHxg@mail.gmail.com/6-v2-0005-Tests-for-parallel-copy.patch)
download | inline diff:
From ec2489bc2316da2aaa959e1e1c53df1f0a801d8a Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:19:39 +0530
Subject: [PATCH v2 5/5] Tests for parallel copy.
This patch has the tests for parallel copy.
---
src/test/regress/expected/copy2.out | 206 ++++++++++++++++++++++++++++++++++-
src/test/regress/input/copy.source | 12 +++
src/test/regress/output/copy.source | 12 +++
src/test/regress/sql/copy2.sql | 208 +++++++++++++++++++++++++++++++++++-
4 files changed, 430 insertions(+), 8 deletions(-)
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index e40287d..0e01fa0 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -254,18 +254,32 @@ It is "perfect".|
"It is ""perfect""."," "
"",
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+SELECT * FROM testnl;
+ a | b | c
+---+----------------------+---
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+(2 rows)
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
COPY testeoc TO stdout CSV;
a\.
\.b
c\.d
"\."
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
1 \\0
@@ -280,6 +294,15 @@ SELECT * FROM testnull;
|
(4 rows)
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+SELECT * FROM testnull;
+ a | b
+----+----
+ 42 | \0
+ |
+(2 rows)
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -349,6 +372,34 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ a2
+ b
+(2 rows)
+
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+COMMIT;
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
SELECT * FROM vistest;
a
@@ -409,7 +460,7 @@ SELECT * FROM vistest;
(2 rows)
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -420,6 +471,8 @@ CREATE TEMP TABLE forcetest (
-- should succeed with no effect ("b" remains an empty string, "c" remains NULL)
BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
b | c
@@ -430,6 +483,8 @@ SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
c | d
@@ -486,6 +541,31 @@ select * from check_con_tbl;
(2 rows)
+\d+ check_con_tbl
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | | | plain | |
+Check constraints:
+ "check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
+
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":1}
+NOTICE: input = {"f1":null}
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":0}
+ERROR: new row for relation "check_con_tbl" violates check constraint "check_con_tbl_check"
+DETAIL: Failing row contains (0).
+CONTEXT: COPY check_con_tbl, line 1: "0"
+parallel worker
+select * from check_con_tbl;
+ f1
+----
+ 1
+
+(2 rows)
+
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
CREATE ROLE regress_rls_copy_user_colperms;
@@ -600,8 +680,126 @@ SELECT * FROM instead_of_insert_tbl;
(2 rows)
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+ERROR: argument to option "parallel" must be a positive integer greater than zero
+LINE 1: COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0'...
+ ^
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+COPY temp_test (a) from stdin with (PARALLEL 1);
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+ERROR: column "xyz" of relation "test_parallel_copy" does not exist
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+ERROR: column "d" specified more than once
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: invalid input syntax for type integer: ""
+CONTEXT: COPY test_parallel_copy, line 0, column a: ""
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2000 230 23 23"
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2001 231 \N \N"
+parallel worker
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: extra data after last expected column
+CONTEXT: COPY test_parallel_copy, line 1: "2002 232 40 50 60 70 80"
+parallel worker
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ERROR: column "f" does not exist
+LINE 1: ..._parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ERROR: missing FROM-clause entry for table "x"
+LINE 1: ...rallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+ERROR: cannot use subquery in COPY FROM WHERE condition
+LINE 1: ...arallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT...
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+ERROR: set-returning functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...lel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_s...
+ ^
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+ERROR: window functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...rallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number...
+ ^
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+ a | b | c | d | e
+-------+----+------------+---------+---------
+ 1 | 11 | test_c1 | test_d1 | test_e1
+ 2 | 12 | test_c2 | test_d2 | test_e2
+ | 3 | stuff | test_d3 |
+ | 4 | stuff | test_d4 |
+ | 5 | stuff | test_d5 |
+ | | 45 | 80 | 90
+ | | x | \x | \x
+ | | , | \, | \
+ 3000 | | c | |
+ 4000 | | C | |
+ 4001 | 1 | empty | |
+ 4002 | 2 | null | |
+ 4003 | 3 | Backslash | \ | \
+ 4004 | 4 | BackslashX | \X | \X
+ 4005 | 5 | N | N | N
+ 4006 | 6 | BackslashN | \N | \N
+ 4007 | 7 | XX | XX | XX
+ 4008 | 8 | Delimiter | : | :
+ 50004 | 25 | 35 | 45 | 55
+ 60004 | 25 | 35 | 45 | 55
+ 60005 | 26 | 36 | 46 | 56
+(21 rows)
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source
index a1d529a..159c058 100644
--- a/src/test/regress/input/copy.source
+++ b/src/test/regress/input/copy.source
@@ -15,6 +15,13 @@ DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+
+SELECT COUNT(*) FROM tenk1;
+
+TRUNCATE tenk1;
+
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
@@ -159,6 +166,11 @@ truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
+
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source
index 938d355..c3003fe 100644
--- a/src/test/regress/output/copy.source
+++ b/src/test/regress/output/copy.source
@@ -9,6 +9,15 @@ COPY onek FROM '@abs_srcdir@/data/onek.data';
COPY onek TO '@abs_builddir@/results/onek.data';
DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+SELECT COUNT(*) FROM tenk1;
+ count
+-------
+ 10000
+(1 row)
+
+TRUNCATE tenk1;
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
COPY person FROM '@abs_srcdir@/data/person.data';
@@ -113,6 +122,9 @@ insert into parted_copytest select x,1,'One' from generate_series(1011,1020) x;
copy (select * from parted_copytest order by a) to '@abs_builddir@/results/parted_copytest.csv';
truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql
index 902f4fa..7015698 100644
--- a/src/test/regress/sql/copy2.sql
+++ b/src/test/regress/sql/copy2.sql
@@ -157,7 +157,7 @@ COPY y TO stdout (FORMAT CSV, FORCE_QUOTE *);
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
1,"a field with two LFs
@@ -165,8 +165,16 @@ COPY testnl FROM stdin CSV;
inside",2
\.
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+1,"a field with two LFs
+
+inside",2
+\.
+
+SELECT * FROM testnl;
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
a\.
@@ -175,11 +183,19 @@ c\.d
"\."
\.
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+a\.
+\.b
+c\.d
+"\."
+\.
+
COPY testeoc TO stdout CSV;
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
@@ -191,6 +207,14 @@ COPY testnull FROM stdin WITH NULL AS E'\\0';
SELECT * FROM testnull;
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+42 \\0
+\0 \0
+\.
+
+SELECT * FROM testnull;
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -235,6 +259,23 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+a2
+b
+\.
+SELECT * FROM vistest;
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+d2
+e
+\.
+SELECT * FROM vistest;
+COMMIT;
+SELECT * FROM vistest;
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
x
y
@@ -284,7 +325,7 @@ SELECT * FROM vistest;
COMMIT;
SELECT * FROM vistest;
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -297,6 +338,10 @@ BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
1,,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
+1,,""
+\.
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
@@ -304,6 +349,10 @@ BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
2,'a',,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
+2,'a',,""
+\.
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
-- should fail with not-null constraint violation
@@ -339,6 +388,16 @@ copy check_con_tbl from stdin;
0
\.
select * from check_con_tbl;
+\d+ check_con_tbl
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+1
+\N
+\.
+copy check_con_tbl from stdin with (parallel 1);
+0
+\.
+select * from check_con_tbl;
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
@@ -440,8 +499,149 @@ test1
SELECT * FROM instead_of_insert_tbl;
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+1 11 test_c1 test_d1 test_e1
+2 12 test_c2 test_d2 test_e2
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+3 test_d3
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+4 test_d4
+5 test_d5
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+b d
+\.
+
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+10
+\.
+
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+60,60,60
+\.
+
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+60
+\.
+
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+
+COPY temp_test (a) from stdin with (PARALLEL 1);
+10
+\.
+
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2000 230 23 23
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2001 231 \N \N
+\.
+
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2002 232 40 50 60 70 80
+\.
+
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+x,45,80,90
+x,\x,\\x,\\\x
+x,\,,\\\,,\\
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+3000;;c;;
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+4000:\X:C:\X:\X
+4001:1:empty::
+4002:2:null:\X:\X
+4003:3:Backslash:\\:\\
+4004:4:BackslashX:\\X:\\X
+4005:5:N:\N:\N
+4006:6:BackslashN:\\N:\\N
+4007:7:XX:\XX:\XX
+4008:8:Delimiter:\::\:
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+50003 24 34 44 54
+50004 25 35 45 55
+50005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+60001 22 32 42 52
+60002 23 33 43 53
+60003 24 34 44 54
+60004 25 35 45 55
+60005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
--
1.8.3.1
[application/x-patch] v2-0006-Parallel-Copy-For-Binary-Format-Files.patch (27.2K, ../../CALj2ACUKKWnju+Xbm1tOtDQj=FxSVBZ1acZcF77RWuN6MCDHxg@mail.gmail.com/7-v2-0006-Parallel-Copy-For-Binary-Format-Files.patch)
download | inline diff:
From 26a401f1ece2dfca2414805c5ae2c71f156e9ae6 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <[email protected]>
Date: Mon, 3 Aug 2020 11:58:35 +0530
Subject: [PATCH v2] Parallel Copy For Binary Format Files
Leader reads data from the file into the DSM data blocks each of 64K size.
It also identifies each tuple data block id, start offset, end offset,
tuple size and updates this information in the ring data structure.
Workers parallelly read the tuple information from the ring data structure,
the actual tuple data from the data blocks and parallelly insert the tuples
into the table.
---
src/backend/commands/copy.c | 687 +++++++++++++++++++++++++++++++-----
1 file changed, 602 insertions(+), 85 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index c65fc9866f..af24c20a3f 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -262,6 +262,17 @@ typedef struct ParallelCopyLineBuf
uint64 cur_lineno; /* line number for error messages */
}ParallelCopyLineBuf;
+/*
+ * Represents the usage mode for CopyReadBinaryGetDataBlock.
+ */
+typedef enum FieldInfoType
+{
+ FIELD_NONE = 0,
+ FIELD_COUNT,
+ FIELD_SIZE,
+ FIELD_DATA
+} FieldInfoType;
+
/*
* Parallel copy data information.
*/
@@ -283,6 +294,9 @@ typedef struct ParallelCopyData
/* Current position in worker_line_buf */
uint32 worker_line_buf_pos;
+
+ /* For binary formatted files */
+ ParallelCopyDataBlock *curr_data_block;
}ParallelCopyData;
/*
@@ -447,6 +461,7 @@ typedef struct SerializedParallelCopyState
/* Working state for COPY FROM */
AttrNumber num_defaults;
Oid relid;
+ bool binary;
}SerializedParallelCopyState;
/* DestReceiver for COPY (query) TO */
@@ -521,7 +536,6 @@ typedef struct CopyMultiInsertInfo
int ti_options; /* table insert options */
} CopyMultiInsertInfo;
-
/*
* These macros centralize code used to process line_buf and raw_buf buffers.
* They are macros because they often do continue/break control and to avoid
@@ -651,8 +665,110 @@ else \
/* End parallel copy Macros */
-static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
+/*
+ * CHECK_FIELD_COUNT - Handles the error cases for field count
+ * for binary format files.
+ */
+#define CHECK_FIELD_COUNT \
+{\
+ if (fld_count == -1) \
+ { \
+ if (IsParallelCopy() && \
+ !IsLeader()) \
+ return true; \
+ else if (IsParallelCopy() && \
+ IsLeader()) \
+ { \
+ if (cstate->pcdata->curr_data_block->data[cstate->raw_buf_index + sizeof(fld_count)] != 0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("received copy data after EOF marker"))); \
+ return true; \
+ } \
+ else \
+ { \
+ /* \
+ * Received EOF marker. In a V3-protocol copy, wait for the \
+ * protocol-level EOF, and complain if it doesn't come \
+ * immediately. This ensures that we correctly handle CopyFail, \
+ * if client chooses to send that now. \
+ * \
+ * Note that we MUST NOT try to read more data in an old-protocol \
+ * copy, since there is no protocol-level EOF marker then. We \
+ * could go either way for copy from file, but choose to throw \
+ * error if there's data after the EOF marker, for consistency \
+ * with the new-protocol case. \
+ */ \
+ char dummy; \
+ if (cstate->copy_dest != COPY_OLD_FE && \
+ CopyReadBinaryData(cstate, &dummy, 1) > 0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("received copy data after EOF marker"))); \
+ return false; \
+ } \
+ } \
+ if (fld_count != cstate->max_fields) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("row field count is %d, expected %d", \
+ (int) fld_count, cstate->max_fields))); \
+}
+
+/*
+ * CHECK_FIELD_SIZE - Handles the error case for field size
+ * for binary format files.
+ */
+#define CHECK_FIELD_SIZE(fld_size) \
+{ \
+ if (fld_size < -1) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("invalid field size")));\
+}
+/*
+ * EOF_ERROR - Error statement for EOF for binary format
+ * files.
+ */
+#define EOF_ERROR \
+{ \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("unexpected EOF in COPY data")));\
+}
+
+/*
+ * GET_RAW_BUF_INDEX - Calculates the raw buf index for the cases
+ * where the data spread is across multiple data blocks.
+ */
+#define GET_RAW_BUF_INDEX(raw_buf_index, fld_size, required_blks, curr_blk_bytes) \
+{ \
+ raw_buf_index = fld_size - (((required_blks - 1) * DATA_BLOCK_SIZE) + curr_blk_bytes); \
+}
+
+/*
+ * GET_REQUIRED_BLOCKS - Calculates the number of data
+ * blocks required for the cases where the data spread
+ * is across multiple data blocks.
+ */
+#define GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes) \
+{ \
+ /* \
+ * field size can spread across multiple data blocks, \
+ * calculate the number of required data blocks and try to get \
+ * those many data blocks. \
+ */ \
+ required_blks = (int32)(fld_size - curr_blk_bytes)/(int32)DATA_BLOCK_SIZE; \
+ /* \
+ * check if we need the data block for the field data \
+ * bytes that are not modulus of data block size. \
+ */ \
+ if ((fld_size - curr_blk_bytes)%DATA_BLOCK_SIZE != 0) \
+ required_blks++; \
+}
+
+static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
/* non-export function prototypes */
static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel,
@@ -708,6 +824,14 @@ static void ExecBeforeStmtTrigger(CopyState cstate);
static void CheckTargetRelValidity(CopyState cstate);
static void PopulateCstateCatalogInfo(CopyState cstate);
static pg_attribute_always_inline uint32 GetLinePosition(CopyState cstate);
+static uint32 WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info);
+static bool CopyReadBinaryTupleLeader(CopyState cstate);
+static void CopyReadBinaryFindTupleSize(CopyState cstate, uint32 *line_size);
+static bool CopyReadBinaryTupleWorker(CopyState cstate, Datum *values, bool *nulls);
+static Datum CopyReadBinaryAttributeWorker(CopyState cstate, FmgrInfo *flinfo,
+ Oid typioparam, int32 typmod, bool *isnull);
+static void CopyReadBinaryGetDataBlock(CopyState cstate, FieldInfoType field_info);
+
/*
* SerializeParallelCopyState - Copy shared_cstate using cstate information.
*/
@@ -725,6 +849,7 @@ SerializeParallelCopyState(CopyState cstate, SerializedParallelCopyState *shared
shared_cstate->convert_selectively = cstate->convert_selectively;
shared_cstate->num_defaults = cstate->num_defaults;
shared_cstate->relid = cstate->pcdata->relid;
+ shared_cstate->binary = cstate->binary;
}
/*
@@ -884,8 +1009,8 @@ FindInsertMethod(CopyState cstate)
static pg_attribute_always_inline bool
IsParallelCopyAllowed(CopyState cstate)
{
- /* Parallel copy not allowed for frontend (2.0 protocol) & binary option. */
- if ((cstate->copy_dest == COPY_OLD_FE) || cstate->binary)
+ /* Parallel copy not allowed for frontend (2.0 protocol). */
+ if (cstate->copy_dest == COPY_OLD_FE)
return false;
/* Check if copy is into foreign table or temporary table. */
@@ -1155,6 +1280,7 @@ InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
cstate->convert_selectively = shared_cstate->convert_selectively;
cstate->num_defaults = shared_cstate->num_defaults;
pcdata->relid = shared_cstate->relid;
+ cstate->binary = shared_cstate->binary;
PopulateCommonCstateInfo(cstate, tup_desc, attnamelist);
@@ -1541,35 +1667,72 @@ ParallelCopyFrom(CopyState cstate)
ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+ /* raw_buf is not used in parallel copy, instead data blocks are used.*/
+ pfree(cstate->raw_buf);
+
/* Execute the before statement triggers from the leader */
ExecBeforeStmtTrigger(cstate);
- /* On input just throw the header line away. */
- if (cstate->cur_lineno == 0 && cstate->header_line)
+ if (!cstate->binary)
{
- cstate->cur_lineno++;
- if (CopyReadLine(cstate))
+ /* On input just throw the header line away. */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
{
- pcshared_info->is_read_in_progress = false;
- return; /* done */
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ {
+ pcshared_info->is_read_in_progress = false;
+ return; /* done */
+ }
}
- }
- for (;;)
- {
- bool done;
- cstate->cur_lineno++;
+ for (;;)
+ {
+ bool done;
+ cstate->cur_lineno++;
- /* Actually read the line into memory here. */
- done = CopyReadLine(cstate);
+ /* Actually read the line into memory here. */
+ done = CopyReadLine(cstate);
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ break;
+ }
+ }
+ else
+ {
/*
- * EOF at start of line means we're done. If we see EOF after some
- * characters, we act as though it was newline followed by EOF, ie,
- * process the line and then exit loop on next iteration.
+ * Binary Format Files.
+ * For parallel copy leader, fill in the error
+ * context information here, in case any failures
+ * while determining tuple offsets, leader
+ * would throw the errors with proper context.
*/
- if (done && cstate->line_buf.len == 0)
- break;
+ ErrorContextCallback errcallback;
+ errcallback.callback = CopyFromErrorCallback;
+ errcallback.arg = (void *) cstate;
+ errcallback.previous = error_context_stack;
+ error_context_stack = &errcallback;
+ cstate->pcdata->curr_data_block = NULL;
+ cstate->raw_buf_index = 0;
+ pcshared_info->populated = 0;
+ cstate->cur_lineno = 0;
+ cstate->max_fields = list_length(cstate->attnumlist);
+
+ for (;;)
+ {
+ bool eof = false;
+ cstate->cur_lineno++;
+
+ eof = CopyReadBinaryTupleLeader(cstate);
+
+ if (eof)
+ break;
+ }
}
pcshared_info->is_read_in_progress = false;
@@ -1577,7 +1740,355 @@ ParallelCopyFrom(CopyState cstate)
}
/*
- * GetLinePosition - return the line position that worker should process.
+ * CopyReadBinaryGetDataBlock - gets a new block, updates
+ * the current offset, calculates the skip bytes.
+ */
+static void
+CopyReadBinaryGetDataBlock(CopyState cstate, FieldInfoType field_info)
+{
+ ParallelCopyDataBlock *data_block = NULL;
+ ParallelCopyDataBlock *curr_data_block = cstate->pcdata->curr_data_block;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ uint8 move_bytes = 0;
+ uint32 block_pos;
+ uint32 prev_block_pos;
+ int read_bytes = 0;
+
+ prev_block_pos = pcshared_info->cur_block_pos;
+
+ block_pos = WaitGetFreeCopyBlock(pcshared_info);
+
+ if (field_info == FIELD_SIZE || field_info == FIELD_COUNT)
+ move_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+
+ if (curr_data_block != NULL)
+ curr_data_block->skip_bytes = move_bytes;
+
+ data_block = &pcshared_info->data_blocks[block_pos];
+
+ if (move_bytes > 0 && curr_data_block != NULL)
+ memmove(&data_block->data[0], &curr_data_block->data[cstate->raw_buf_index], move_bytes);
+
+ elog(DEBUG1, "LEADER - field info %d is spread across data blocks - moved %d bytes from current block %u to %u block",
+ field_info, move_bytes, prev_block_pos, block_pos);
+
+ read_bytes = CopyGetData(cstate, &data_block->data[move_bytes], 1, (DATA_BLOCK_SIZE - move_bytes));
+
+ if (field_info == FIELD_NONE && cstate->reached_eof)
+ return;
+
+ if (cstate->reached_eof)
+ EOF_ERROR;
+
+ elog(DEBUG1, "LEADER - bytes read from file %d", read_bytes);
+
+ if(field_info == FIELD_SIZE || field_info == FIELD_DATA)
+ {
+ ParallelCopyDataBlock *prev_data_block = NULL;
+ prev_data_block = curr_data_block;
+ prev_data_block->following_block = block_pos;
+
+ if (prev_data_block->curr_blk_completed == false)
+ prev_data_block->curr_blk_completed = true;
+
+ pg_atomic_add_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ }
+
+ cstate->pcdata->curr_data_block = data_block;
+ cstate->raw_buf_index = 0;
+}
+
+/*
+ * CopyReadBinaryTupleLeader - leader reads data from binary formatted file
+ * to data blocks and identifies tuple boundaries/offsets so that workers
+ * can work on the data blocks data.
+ */
+static bool
+CopyReadBinaryTupleLeader(CopyState cstate)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ int16 fld_count;
+ uint32 line_size = 0;
+ uint32 start_block_pos;
+ uint32 start_offset;
+
+ if (cstate->pcdata->curr_data_block == NULL)
+ {
+ CopyReadBinaryGetDataBlock(cstate, FIELD_NONE);
+
+ /*
+ * no data is read from file here. one possibility
+ * to be here could be that the binary file just
+ * has a valid signature but nothing else.
+ */
+ if (cstate->reached_eof)
+ return true;
+ }
+
+ if ((cstate->raw_buf_index + sizeof(fld_count)) >= DATA_BLOCK_SIZE)
+ CopyReadBinaryGetDataBlock(cstate, FIELD_COUNT);
+
+ memcpy(&fld_count, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_count));
+ fld_count = (int16) pg_ntoh16(fld_count);
+ CHECK_FIELD_COUNT;
+ start_offset = cstate->raw_buf_index;
+ cstate->raw_buf_index += sizeof(fld_count);
+ line_size += sizeof(fld_count);
+ start_block_pos = pcshared_info->cur_block_pos;
+
+ CopyReadBinaryFindTupleSize(cstate, &line_size);
+
+ pg_atomic_add_fetch_u32(&cstate->pcdata->curr_data_block->unprocessed_line_parts, 1);
+
+ if (line_size > 0)
+ {
+ int line_pos = UpdateBlockInLineInfo(cstate,
+ start_block_pos,
+ start_offset,
+ line_size,
+ LINE_LEADER_POPULATED);
+
+ pcshared_info->populated++;
+ elog(DEBUG1, "LEADER - adding - block:%u, offset:%u, line size:%u line position:%d",
+ start_block_pos, start_offset, line_size, line_pos);
+ }
+
+ return false;
+}
+
+/*
+ * CopyReadBinaryFindTupleSize - leader identifies boundaries/
+ * offsets for each attribute/column and finally results in the
+ * tuple/row size. It moves on to next data block if the attribute/
+ * column is spread across data blocks.
+ */
+static void
+CopyReadBinaryFindTupleSize(CopyState cstate, uint32 *line_size)
+{
+ int32 fld_size;
+ ListCell *cur;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ foreach(cur, cstate->attnumlist)
+ {
+ int att_num = lfirst_int(cur);
+ Form_pg_attribute att = TupleDescAttr(tup_desc, (att_num - 1));
+ cstate->cur_attname = NameStr(att->attname);
+ fld_size = 0;
+
+ if ((cstate->raw_buf_index + sizeof(fld_size)) >= DATA_BLOCK_SIZE)
+ CopyReadBinaryGetDataBlock(cstate, FIELD_SIZE);
+
+ memcpy(&fld_size, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_size));
+ cstate->raw_buf_index += sizeof(fld_size);
+ *line_size += sizeof(fld_size);
+ fld_size = (int32) pg_ntoh32(fld_size);
+
+ CHECK_FIELD_SIZE(fld_size);
+
+ *line_size += fld_size;
+
+ if ((DATA_BLOCK_SIZE - cstate->raw_buf_index) >= fld_size)
+ {
+ cstate->raw_buf_index += fld_size;
+ elog(DEBUG1, "LEADER - tuple lies in he same data block");
+ }
+ else
+ {
+ int32 required_blks = 0;
+ int32 curr_blk_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+ int i = 0;
+
+ GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes);
+
+ i = required_blks;
+
+ while(i > 0)
+ {
+ CopyReadBinaryGetDataBlock(cstate, FIELD_DATA);
+ i--;
+ }
+
+ GET_RAW_BUF_INDEX(cstate->raw_buf_index, fld_size, required_blks, curr_blk_bytes);
+
+ /*
+ * raw_buf_index should never cross data block size,
+ * as the required number of data blocks would have
+ * been obtained in the above while loop.
+ */
+ Assert(cstate->raw_buf_index <= DATA_BLOCK_SIZE);
+ }
+ cstate->cur_attname = NULL;
+ }
+}
+
+/*
+ * CopyReadBinaryTupleWorker - each worker reads data from data blocks after
+ * getting leader-identified tuple offsets from ring data structure.
+ */
+static bool
+CopyReadBinaryTupleWorker(CopyState cstate, Datum *values, bool *nulls)
+{
+ uint32 line_pos;
+ ParallelCopyLineBoundary *line_info;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ int16 fld_count;
+ ListCell *cur;
+ FmgrInfo *in_functions = cstate->in_functions;
+ Oid *typioparams = cstate->typioparams;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ line_pos = GetLinePosition(cstate);
+
+ if (line_pos == -1)
+ return true;
+
+ line_info = &pcshared_info->line_boundaries.ring[line_pos];
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[line_info->first_block];
+ cstate->raw_buf_index = line_info->start_offset;
+
+ if (cstate->raw_buf_index + sizeof(fld_count) >= DATA_BLOCK_SIZE)
+ {
+ /*
+ * The case where field count spread across datablocks should never occur,
+ * as the leader would have moved it to next block. this code exists for
+ * debugging purposes only.
+ */
+ elog(DEBUG1, "WORKER - field count spread across datablocks should never occur");
+ }
+
+ memcpy(&fld_count, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_count));
+ fld_count = (int16) pg_ntoh16(fld_count);
+
+ CHECK_FIELD_COUNT;
+
+ cstate->raw_buf_index += sizeof(fld_count);
+
+ foreach(cur, cstate->attnumlist)
+ {
+ int att_num = lfirst_int(cur);
+ int m = att_num - 1;
+ Form_pg_attribute att = TupleDescAttr(tup_desc, m);
+ cstate->cur_attname = NameStr(att->attname);
+
+ values[m] = CopyReadBinaryAttributeWorker(cstate,
+ &in_functions[m],
+ typioparams[m],
+ att->atttypmod,
+ &nulls[m]);
+ cstate->cur_attname = NULL;
+ }
+
+ pg_atomic_sub_fetch_u32(&cstate->pcdata->curr_data_block->unprocessed_line_parts, 1);
+ line_info->start_offset = -1;
+ pg_atomic_write_u32(&line_info->line_state, LINE_WORKER_PROCESSED);
+ pg_atomic_write_u32(&line_info->line_size, -1);
+ pg_atomic_add_fetch_u64(&pcshared_info->total_worker_processed, 1);
+
+ return false;
+}
+
+/*
+ * CopyReadBinaryAttributeWorker - leader identifies boundaries/offsets
+ * for each attribute/column, it moves on to next data block if the
+ * attribute/column is spread across data blocks.
+ */
+static Datum
+CopyReadBinaryAttributeWorker(CopyState cstate, FmgrInfo *flinfo,
+ Oid typioparam, int32 typmod, bool *isnull)
+{
+ int32 fld_size;
+ Datum result;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+
+ if ((cstate->raw_buf_index + sizeof(fld_size)) >= DATA_BLOCK_SIZE)
+ {
+ ParallelCopyDataBlock *prev_data_block = cstate->pcdata->curr_data_block;
+ elog(DEBUG1, "WORKER - field size is spread across data blocks");
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[prev_data_block->following_block];
+ pg_atomic_sub_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ cstate->raw_buf_index = 0;
+ }
+
+ memcpy(&fld_size, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_size));
+ fld_size = (int32) pg_ntoh32(fld_size);
+
+ CHECK_FIELD_SIZE(fld_size);
+
+ cstate->raw_buf_index += sizeof(fld_size);
+
+ /* reset attribute_buf to empty, and load raw data in it */
+ resetStringInfo(&cstate->attribute_buf);
+
+ enlargeStringInfo(&cstate->attribute_buf, fld_size);
+
+ if ((DATA_BLOCK_SIZE - cstate->raw_buf_index) >= fld_size)
+ {
+ elog(DEBUG1, "WORKER - tuple lies in single data block");
+ memcpy(&cstate->attribute_buf.data[0],&cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], fld_size);
+ cstate->raw_buf_index += fld_size;
+ }
+ else
+ {
+ uint32 att_buf_idx = 0;
+ uint32 copy_bytes = 0;
+ int32 required_blks = 0;
+ int32 curr_blk_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+ ParallelCopyDataBlock *prev_data_block = NULL;
+ int i = 0;
+
+ GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes);
+
+ i = required_blks;
+ prev_data_block = cstate->pcdata->curr_data_block;
+ elog(DEBUG1, "WORKER - tuple is spread across data blocks");
+ memcpy(&cstate->attribute_buf.data[0], &prev_data_block->data[cstate->raw_buf_index],
+ curr_blk_bytes);
+ copy_bytes = curr_blk_bytes;
+ att_buf_idx = curr_blk_bytes;
+
+ while (i>0)
+ {
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[prev_data_block->following_block];
+ pg_atomic_sub_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ cstate->raw_buf_index = 0;
+ copy_bytes = fld_size - att_buf_idx;
+
+ /*
+ * the bytes that are yet to be taken into att buff are more than
+ * the entire data block size, but only take the data block size
+ * elements.
+ */
+ if (copy_bytes >= DATA_BLOCK_SIZE)
+ copy_bytes = DATA_BLOCK_SIZE;
+
+ memcpy(&cstate->attribute_buf.data[att_buf_idx],
+ &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], copy_bytes);
+ att_buf_idx += copy_bytes;
+ i--;
+ }
+ GET_RAW_BUF_INDEX(cstate->raw_buf_index, fld_size, required_blks, curr_blk_bytes);
+ }
+
+ cstate->attribute_buf.len = fld_size;
+ cstate->attribute_buf.data[fld_size] = '\0';
+
+ /* Call the column type's binary input converter */
+ result = ReceiveFunctionCall(flinfo, &cstate->attribute_buf,
+ typioparam, typmod);
+
+ /* Trouble if it didn't eat the whole buffer */
+ if (cstate->attribute_buf.cursor != cstate->attribute_buf.len)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("incorrect binary data format")));
+
+ *isnull = false;
+ return result;
+}
+
+/*
+ * GetLinePosition - return the line position that worker should pcdata->process.
*/
static uint32
GetLinePosition(CopyState cstate)
@@ -1665,7 +2176,9 @@ GetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
{
dataBlkPtr->curr_blk_completed = false;
dataBlkPtr->skip_bytes = 0;
+ dataBlkPtr->following_block = -1;
pcshared_info->cur_block_pos = block_pos;
+ MemSet(&dataBlkPtr->data[0], 0, DATA_BLOCK_SIZE);
return block_pos;
}
@@ -2181,10 +2694,26 @@ CopyGetInt32(CopyState cstate, int32 *val)
{
uint32 buf;
- if (CopyReadBinaryData(cstate, (char *) &buf, sizeof(buf)) != sizeof(buf))
+ /*
+ * For parallel copy, avoid reading data to raw buf, read directly
+ * from file, later the data will be read to parallel copy data
+ * buffers.
+ */
+ if (cstate->nworkers > 0)
{
- *val = 0; /* suppress compiler warning */
- return false;
+ if (CopyGetData(cstate, &buf, sizeof(buf), sizeof(buf)) != sizeof(buf))
+ {
+ *val = 0; /* suppress compiler warning */
+ return false;
+ }
+ }
+ else
+ {
+ if (CopyReadBinaryData(cstate, (char *) &buf, sizeof(buf)) != sizeof(buf))
+ {
+ *val = 0; /* suppress compiler warning */
+ return false;
+ }
}
*val = (int32) pg_ntoh32(buf);
return true;
@@ -2573,7 +3102,15 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
EndParallelCopy(pcxt);
}
else
+ {
+ /*
+ * Reset nworkers to -1 here. This is useful in cases where user
+ * specifies parallel workers, but, no worker is picked up, so go
+ * back to non parallel mode value of nworkers.
+ */
+ cstate->nworkers = -1;
*processed = CopyFrom(cstate); /* copy from file to database */
+ }
EndCopyFrom(cstate);
}
@@ -5052,7 +5589,7 @@ BeginCopyFrom(ParseState *pstate,
* only in text mode.
*/
initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (IsParallelCopy()) ? NULL : (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
cstate->raw_buf_index = cstate->raw_buf_len = 0;
if (!cstate->binary)
{
@@ -5132,7 +5669,7 @@ BeginCopyFrom(ParseState *pstate,
int32 tmp;
/* Signature */
- if (CopyReadBinaryData(cstate, readSig, 11) != 11 ||
+ if (CopyGetData(cstate, readSig, 11, 11) != 11 ||
memcmp(readSig, BinarySignature, 11) != 0)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
@@ -5160,7 +5697,7 @@ BeginCopyFrom(ParseState *pstate,
/* Skip extension header, if present */
while (tmp-- > 0)
{
- if (CopyReadBinaryData(cstate, readSig, 1) != 1)
+ if (CopyGetData(cstate, readSig, 1, 1) != 1)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("invalid COPY file header (wrong length)")));
@@ -5357,60 +5894,45 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext,
else
{
/* binary */
- int16 fld_count;
- ListCell *cur;
-
cstate->cur_lineno++;
+ cstate->max_fields = list_length(cstate->attnumlist);
- if (!CopyGetInt16(cstate, &fld_count))
+ if (!IsParallelCopy())
{
- /* EOF detected (end of file, or protocol-level EOF) */
- return false;
- }
+ int16 fld_count;
+ ListCell *cur;
- if (fld_count == -1)
- {
- /*
- * Received EOF marker. In a V3-protocol copy, wait for the
- * protocol-level EOF, and complain if it doesn't come
- * immediately. This ensures that we correctly handle CopyFail,
- * if client chooses to send that now.
- *
- * Note that we MUST NOT try to read more data in an old-protocol
- * copy, since there is no protocol-level EOF marker then. We
- * could go either way for copy from file, but choose to throw
- * error if there's data after the EOF marker, for consistency
- * with the new-protocol case.
- */
- char dummy;
-
- if (cstate->copy_dest != COPY_OLD_FE &&
- CopyReadBinaryData(cstate, &dummy, 1) > 0)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("received copy data after EOF marker")));
- return false;
- }
+ if (!CopyGetInt16(cstate, &fld_count))
+ {
+ /* EOF detected (end of file, or protocol-level EOF) */
+ return false;
+ }
- if (fld_count != attr_count)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("row field count is %d, expected %d",
- (int) fld_count, attr_count)));
+ CHECK_FIELD_COUNT;
- foreach(cur, cstate->attnumlist)
+ foreach(cur, cstate->attnumlist)
+ {
+ int attnum = lfirst_int(cur);
+ int m = attnum - 1;
+ Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+
+ cstate->cur_attname = NameStr(att->attname);
+ values[m] = CopyReadBinaryAttribute(cstate,
+ &in_functions[m],
+ typioparams[m],
+ att->atttypmod,
+ &nulls[m]);
+ cstate->cur_attname = NULL;
+ }
+ }
+ else
{
- int attnum = lfirst_int(cur);
- int m = attnum - 1;
- Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+ bool eof = false;
- cstate->cur_attname = NameStr(att->attname);
- values[m] = CopyReadBinaryAttribute(cstate,
- &in_functions[m],
- typioparams[m],
- att->atttypmod,
- &nulls[m]);
- cstate->cur_attname = NULL;
+ eof = CopyReadBinaryTupleWorker(cstate, values, nulls);
+
+ if (eof)
+ return false;
}
}
@@ -6410,18 +6932,15 @@ CopyReadBinaryAttribute(CopyState cstate, FmgrInfo *flinfo,
Datum result;
if (!CopyGetInt32(cstate, &fld_size))
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("unexpected EOF in COPY data")));
+ EOF_ERROR;
+
if (fld_size == -1)
{
*isnull = true;
return ReceiveFunctionCall(flinfo, NULL, typioparam, typmod);
}
- if (fld_size < 0)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("invalid field size")));
+
+ CHECK_FIELD_SIZE(fld_size);
/* reset attribute_buf to empty, and load raw data in it */
resetStringInfo(&cstate->attribute_buf);
@@ -6429,9 +6948,7 @@ CopyReadBinaryAttribute(CopyState cstate, FmgrInfo *flinfo,
enlargeStringInfo(&cstate->attribute_buf, fld_size);
if (CopyReadBinaryData(cstate, cstate->attribute_buf.data,
fld_size) != fld_size)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("unexpected EOF in COPY data")));
+ EOF_ERROR;
cstate->attribute_buf.len = fld_size;
cstate->attribute_buf.data[fld_size] = '\0';
--
2.25.1
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-04 16:21 Tomas Vondra <[email protected]>
parent: Bharath Rupireddy <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Tomas Vondra @ 2020-08-04 16:21 UTC (permalink / raw)
To: Bharath Rupireddy <[email protected]>; +Cc: vignesh C <[email protected]>; Amit Kapila <[email protected]>; Ashutosh Sharma <[email protected]>; Rafia Sabih <[email protected]>; Andres Freund <[email protected]>; Robert Haas <[email protected]>; Ants Aasma <[email protected]>; Alastair Turner <[email protected]>; Thomas Munro <[email protected]>; PostgreSQL Hackers <[email protected]>
On Mon, Aug 03, 2020 at 12:33:48PM +0530, Bharath Rupireddy wrote:
>On Sat, Aug 1, 2020 at 9:55 AM vignesh C <[email protected]> wrote:
>>
>> The patches were not applying because of the recent commits.
>> I have rebased the patch over head & attached.
>>
>I rebased v2-0006-Parallel-Copy-For-Binary-Format-Files.patch.
>
>Putting together all the patches rebased on to the latest commit
>b8fdee7d0ca8bd2165d46fb1468f75571b706a01. Patches from 0001 to 0005
>are rebased by Vignesh, that are from the previous mail and the patch
>0006 is rebased by me.
>
>Please consider this patch set for further review.
>
I'd suggest incrementing the version every time an updated version is
submitted, even if it's just a rebased version. It makes it clearer
which version of the code is being discussed, etc.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-06 04:49 vignesh C <[email protected]>
parent: Tomas Vondra <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: vignesh C @ 2020-08-06 04:49 UTC (permalink / raw)
To: Tomas Vondra <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Amit Kapila <[email protected]>; Ashutosh Sharma <[email protected]>; Rafia Sabih <[email protected]>; Andres Freund <[email protected]>; Robert Haas <[email protected]>; Ants Aasma <[email protected]>; Alastair Turner <[email protected]>; Thomas Munro <[email protected]>; PostgreSQL Hackers <[email protected]>
On Tue, Aug 4, 2020 at 9:51 PM Tomas Vondra
<[email protected]> wrote:
>
> On Mon, Aug 03, 2020 at 12:33:48PM +0530, Bharath Rupireddy wrote:
> >On Sat, Aug 1, 2020 at 9:55 AM vignesh C <[email protected]> wrote:
> >>
> >> The patches were not applying because of the recent commits.
> >> I have rebased the patch over head & attached.
> >>
> >I rebased v2-0006-Parallel-Copy-For-Binary-Format-Files.patch.
> >
> >Putting together all the patches rebased on to the latest commit
> >b8fdee7d0ca8bd2165d46fb1468f75571b706a01. Patches from 0001 to 0005
> >are rebased by Vignesh, that are from the previous mail and the patch
> >0006 is rebased by me.
> >
> >Please consider this patch set for further review.
> >
>
> I'd suggest incrementing the version every time an updated version is
> submitted, even if it's just a rebased version. It makes it clearer
> which version of the code is being discussed, etc.
Sure, we will take care of this when we are sending the next set of patches.
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-12 03:39 Greg Nancarrow <[email protected]>
parent: vignesh C <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Greg Nancarrow @ 2020-08-12 03:39 UTC (permalink / raw)
To: [email protected]; +Cc: vignesh C <[email protected]>; Bharath Rupireddy <[email protected]>
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: tested, failed
Hi,
I don't claim to yet understand all of the Postgres internals that this patch is updating and interacting with, so I'm still testing and debugging portions of this patch, but would like to give feedback on what I've noticed so far.
I have done some ad-hoc testing of the patch using parallel copies from text/csv/binary files and have not yet struck any execution problems other than some option validation and associated error messages on boundary cases.
One general question that I have: is there a user benefit (over the normal non-parallel COPY) to allowing "COPY ... FROM ... WITH (PARALLEL 1)"?
My following comments are broken down by patch:
(1) v2-0001-Copy-code-readjustment-to-support-parallel-copy.patch
(i) Whilst I can't entirely blame these patches for it (as they are following what is already there), I can't help noticing the use of numerous macros in src/backend/commands/copy.c which paste in multiple lines of code in various places.
It's getting a little out-of-hand. Surely the majority of these would be best inline functions instead?
Perhaps hasn't been done because too many parameters need to be passed - thoughts?
(2) v2-0002-Framework-for-leader-worker-in-parallel-copy.patch
(i) minor point: there are some tabbing/spacing issues in this patch (and the other patches), affecting alignment.
e.g. mixed tabs/spaces and misalignment in PARALLEL_COPY_KEY_xxx definitions
(ii)
+/*
+ * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
+ * block to process to avoid lock contention. This value should be mode of
+ * RINGSIZE, as wrap around cases is currently not handled while selecting the
+ * WORKER_CHUNK_COUNT by the worker.
+ */
+#define WORKER_CHUNK_COUNT 50
"This value should be mode of RINGSIZE ..."
-> typo: mode (mod? should evenly divide into RINGSIZE?)
(iii)
+ * using pg_atomic_compare_exchange_u32, worker will change the sate to
->typo: sate (should be "state")
(iv)
+ errmsg("parallel option supported only for copy from"),
-> suggest change to: errmsg("parallel option is supported only for COPY FROM"),
(v)
+ errno = 0; /* To distinguish success/failure after call */
+ val = strtol(str, &endptr, 10);
+
+ /* Check for various possible errors */
+ if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+ || (errno != 0 && val == 0) ||
+ *endptr)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("improper use of argument to option \"%s\"",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
+
+ if (endptr == str)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("no digits were found in argument to option \"%s\"",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
+
+ cstate->nworkers = (int) val;
+
+ if (cstate->nworkers <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("argument to option \"%s\" must be a positive integer greater than zero",
+ defel->defname),
+ parser_errposition(pstate, defel->location)));
I think this validation code needs to be improved, including the error messages (e.g. when can a "positive integer" NOT be greater than zero?)
There is some overlap in the "no digits were found" case between the two conditions above, depending, for example, if the argument is quoted.
Also, "improper use of argument to option" sounds a bit odd and vague to me.
Finally, not range checking before casting long to int can lead to allowing out-of-range int values like in the following case:
test=# copy mytable from '/myspace/test_pcopy/tmp.dat' (parallel '-2147483648');
ERROR: argument to option "parallel" must be a positive integer greater than zero
LINE 1: copy mytable from '/myspace/test_pcopy/tmp.dat' (parallel '-2...
^
BUT the following is allowed...
test=# copy mytable from '/myspace/test_pcopy/tmp.dat' (parallel '-2147483649');
COPY 1000000
I'd suggest to change the above validation code to do similar validation to that for the CREATE TABLE parallel_workers storage parameter (case RELOPT_TYPE_INT in reloptions.c). Like that code, wouldn't it be best to range-check the integer option value to be within a reasonable range, say 1 to 1024, with a corresponding errdetail message if possible?
(3) v2-0003-Allow-copy-from-command-to-process-data-from-file.patch
(i)
Patch comment says:
"This feature allows the copy from to leverage multiple CPUs in order to copy
data from file/STDIN to a table. This adds a PARALLEL option to COPY FROM
command where the user can specify the number of workers that can be used
to perform the COPY FROM command. Specifying zero as number of workers will
disable parallelism."
BUT - the changes to ProcessCopyOptions() specified in "v2-0002-Framework-for-leader-worker-in-parallel-copy.patch" do not allow zero workers to be specified - you get an error in that case. Patch comment should be updated accordingly.
(ii)
#define GETPROCESSED(processed) \
-return processed;
+if (!IsParallelCopy()) \
+ return processed; \
+else \
+ return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+
I think GETPROCESSED would be better named "RETURNPROCESSED".
(iii)
The below comment seems out- of-date with the current code - is it referring to the loop embedded at the bottom of the current loop that the comment is within?
+ /*
+ * There is a possibility that the above loop has come out because
+ * data_blk_ptr->curr_blk_completed is set, but dataSize read might
+ * be an old value, if data_blk_ptr->curr_blk_completed and the line is
+ * completed, line_size will be set. Read the line_size again to be
+ * sure if it is complete or partial block.
+ */
(iv)
I may be wrong here, but in the following block of code, isn't there a window of opportunity (however small) in which the line_state might be updated (LINE_WORKER_PROCESSED) by another worker just AFTER pg_atomic_read_u32() returns the current line_state which is put into curr_line_state, such that a write_pos update might be missed? And then a race-condition exists for reading/setting line_size (since line_size gets atomically set after line_state is set)?
If I am wrong in thinking this synchronization might not be correct, maybe the comments could be improved here to explain how this code is safe in that respect.
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ curr_line_state = pg_atomic_read_u32(&lineInfo->line_state);
+ if ((write_pos % WORKER_CHUNK_COUNT == 0) &&
+ (curr_line_state == LINE_WORKER_PROCESSED ||
+ curr_line_state == LINE_WORKER_PROCESSING))
+ {
+ pcdata->worker_processed_pos = write_pos;
+ write_pos = (write_pos + WORKER_CHUNK_COUNT) % RINGSIZE;
+ continue;
+ }
+
+ /* Get the size of this line. */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ if (dataSize != 0) /* If not an empty line. */
+ {
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ if (!data_blk_ptr->curr_blk_completed && (dataSize == -1))
+ {
+ /* Wait till the current line or block is added. */
+ COPY_WAIT_TO_PROCESS()
+ continue;
+ }
+ }
+
+ /* Make sure that no worker has consumed this element. */
+ if (pg_atomic_compare_exchange_u32(&lineInfo->line_state,
+ &line_state, LINE_WORKER_PROCESSING))
+ break;
(4) v2-0004-Documentation-for-parallel-copy.patch
(i) I think that it is necessary to mention the "max_worker_processes" option in the description of the COPY statement PARALLEL option.
For example, something like:
+ Perform <command>COPY FROM</command> in parallel using <replaceable
+ class="parameter"> integer</replaceable> background workers. Please
+ note that it is not guaranteed that the number of parallel workers
+ specified in <replaceable class="parameter">integer</replaceable> will
+ be used during execution. It is possible for a copy to run with fewer
+ workers than specified, or even with no workers at all (for example,
+ due to the setting of max_worker_processes). This option is allowed
+ only in <command>COPY FROM</command>.
(5) v2-0005-Tests-for-parallel-copy.patch
(i) None of the provided tests seem to test beyond "PARALLEL 2"
(6) v2-0006-Parallel-Copy-For-Binary-Format-Files.patch
(i) In the ParallelCopyFrom() function, "cstate->raw_buf" is pfree()d:
+ /* raw_buf is not used in parallel copy, instead data blocks are used.*/
+ pfree(cstate->raw_buf);
This comment doesn't seem to be entirely true.
At least for text/csv file COPY FROM, cstate->raw_buf is subsequently referenced in the SetRawBufForLoad() function, which is called by CopyReadLineText():
cur_data_blk_ptr = (cstate->raw_buf) ? &pcshared_info->data_blocks[cur_block_pos] : NULL;
So I think cstate->raw_buf should be set to NULL after being pfree()d, and the comment fixed/adjusted.
(ii) This patch adds some macros (involving parallel copy checks) AFTER the comment:
/* End parallel copy Macros */
Regards,
Greg Nancarrow
Fujitsu Australia
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-14 15:48 vignesh C <[email protected]>
parent: Greg Nancarrow <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: vignesh C @ 2020-08-14 15:48 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
Thanks Greg for reviewing the patch. Please find my thoughts for your comments.
On Wed, Aug 12, 2020 at 9:10 AM Greg Nancarrow <[email protected]> wrote:
> I have done some ad-hoc testing of the patch using parallel copies from text/csv/binary files and have not yet struck any execution problems other than some option validation and associated error messages on boundary cases.
>
> One general question that I have: is there a user benefit (over the normal non-parallel COPY) to allowing "COPY ... FROM ... WITH (PARALLEL 1)"?
>
There will be marginal improvement as worker only need to process the
data, need not do the file reading, file reading would have been done
by the main process. The real improvement can be seen from 2 workers
onwards.
>
> My following comments are broken down by patch:
>
> (1) v2-0001-Copy-code-readjustment-to-support-parallel-copy.patch
>
> (i) Whilst I can't entirely blame these patches for it (as they are following what is already there), I can't help noticing the use of numerous macros in src/backend/commands/copy.c which paste in multiple lines of code in various places.
> It's getting a little out-of-hand. Surely the majority of these would be best inline functions instead?
> Perhaps hasn't been done because too many parameters need to be passed - thoughts?
>
I felt they have used macros mainly because it has a tight loop and
having macros gives better performance. I have added the macros
CLEAR_EOL_LINE, INCREMENTPROCESSED & GETPROCESSED as there will be
slight difference in parallel copy & non parallel copy for these. In
the remaining patches the macor will be extended to include parallel
copy logic. Instead of having checks in the core logic, thought of
keeping as macros so that the readability is good.
>
> (2) v2-0002-Framework-for-leader-worker-in-parallel-copy.patch
>
> (i) minor point: there are some tabbing/spacing issues in this patch (and the other patches), affecting alignment.
> e.g. mixed tabs/spaces and misalignment in PARALLEL_COPY_KEY_xxx definitions
>
Fixed
> (ii)
>
> +/*
> + * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
> + * block to process to avoid lock contention. This value should be mode of
> + * RINGSIZE, as wrap around cases is currently not handled while selecting the
> + * WORKER_CHUNK_COUNT by the worker.
> + */
> +#define WORKER_CHUNK_COUNT 50
>
>
> "This value should be mode of RINGSIZE ..."
>
> -> typo: mode (mod? should evenly divide into RINGSIZE?)
Fixed, changed it to divisible by.
> (iii)
> + * using pg_atomic_compare_exchange_u32, worker will change the sate to
>
> ->typo: sate (should be "state")
Fixed
> (iv)
>
> + errmsg("parallel option supported only for copy from"),
>
> -> suggest change to: errmsg("parallel option is supported only for COPY FROM"),
>
Fixed
> (v)
>
> + errno = 0; /* To distinguish success/failure after call */
> + val = strtol(str, &endptr, 10);
> +
> + /* Check for various possible errors */
> + if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
> + || (errno != 0 && val == 0) ||
> + *endptr)
> + ereport(ERROR,
> + (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> + errmsg("improper use of argument to option \"%s\"",
> + defel->defname),
> + parser_errposition(pstate, defel->location)));
> +
> + if (endptr == str)
> + ereport(ERROR,
> + (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> + errmsg("no digits were found in argument to option \"%s\"",
> + defel->defname),
> + parser_errposition(pstate, defel->location)));
> +
> + cstate->nworkers = (int) val;
> +
> + if (cstate->nworkers <= 0)
> + ereport(ERROR,
> + (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> + errmsg("argument to option \"%s\" must be a positive integer greater than zero",
> + defel->defname),
> + parser_errposition(pstate, defel->location)));
>
>
> I think this validation code needs to be improved, including the error messages (e.g. when can a "positive integer" NOT be greater than zero?)
>
> There is some overlap in the "no digits were found" case between the two conditions above, depending, for example, if the argument is quoted.
> Also, "improper use of argument to option" sounds a bit odd and vague to me.
> Finally, not range checking before casting long to int can lead to allowing out-of-range int values like in the following case:
>
> test=# copy mytable from '/myspace/test_pcopy/tmp.dat' (parallel '-2147483648');
> ERROR: argument to option "parallel" must be a positive integer greater than zero
> LINE 1: copy mytable from '/myspace/test_pcopy/tmp.dat' (parallel '-2...
> ^
> BUT the following is allowed...
>
> test=# copy mytable from '/myspace/test_pcopy/tmp.dat' (parallel '-2147483649');
> COPY 1000000
>
>
> I'd suggest to change the above validation code to do similar validation to that for the CREATE TABLE parallel_workers storage parameter (case RELOPT_TYPE_INT in reloptions.c). Like that code, wouldn't it be best to range-check the integer option value to be within a reasonable range, say 1 to 1024, with a corresponding errdetail message if possible?
>
Fixed, changed as suggested.
> (3) v2-0003-Allow-copy-from-command-to-process-data-from-file.patch
>
> (i)
>
> Patch comment says:
>
> "This feature allows the copy from to leverage multiple CPUs in order to copy
> data from file/STDIN to a table. This adds a PARALLEL option to COPY FROM
> command where the user can specify the number of workers that can be used
> to perform the COPY FROM command. Specifying zero as number of workers will
> disable parallelism."
>
> BUT - the changes to ProcessCopyOptions() specified in "v2-0002-Framework-for-leader-worker-in-parallel-copy.patch" do not allow zero workers to be specified - you get an error in that case. Patch comment should be updated accordingly.
>
Removed "Specifying zero as number of workers will disable
parallelism". As the new value is range from 1 to 1024.
> (ii)
>
> #define GETPROCESSED(processed) \
> -return processed;
> +if (!IsParallelCopy()) \
> + return processed; \
> +else \
> + return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
> +
>
> I think GETPROCESSED would be better named "RETURNPROCESSED".
>
Fixed.
> (iii)
>
> The below comment seems out- of-date with the current code - is it referring to the loop embedded at the bottom of the current loop that the comment is within?
>
> + /*
> + * There is a possibility that the above loop has come out because
> + * data_blk_ptr->curr_blk_completed is set, but dataSize read might
> + * be an old value, if data_blk_ptr->curr_blk_completed and the line is
> + * completed, line_size will be set. Read the line_size again to be
> + * sure if it is complete or partial block.
> + */
>
Updated, it is referring to the embedded loop at the bottom of the current loop.
> (iv)
>
> I may be wrong here, but in the following block of code, isn't there a window of opportunity (however small) in which the line_state might be updated (LINE_WORKER_PROCESSED) by another worker just AFTER pg_atomic_read_u32() returns the current line_state which is put into curr_line_state, such that a write_pos update might be missed? And then a race-condition exists for reading/setting line_size (since line_size gets atomically set after line_state is set)?
> If I am wrong in thinking this synchronization might not be correct, maybe the comments could be improved here to explain how this code is safe in that respect.
>
>
> + /* Get the current line information. */
> + lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
> + curr_line_state = pg_atomic_read_u32(&lineInfo->line_state);
> + if ((write_pos % WORKER_CHUNK_COUNT == 0) &&
> + (curr_line_state == LINE_WORKER_PROCESSED ||
> + curr_line_state == LINE_WORKER_PROCESSING))
> + {
> + pcdata->worker_processed_pos = write_pos;
> + write_pos = (write_pos + WORKER_CHUNK_COUNT) % RINGSIZE;
> + continue;
> + }
> +
> + /* Get the size of this line. */
> + dataSize = pg_atomic_read_u32(&lineInfo->line_size);
> +
> + if (dataSize != 0) /* If not an empty line. */
> + {
> + /* Get the block information. */
> + data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
> +
> + if (!data_blk_ptr->curr_blk_completed && (dataSize == -1))
> + {
> + /* Wait till the current line or block is added. */
> + COPY_WAIT_TO_PROCESS()
> + continue;
> + }
> + }
> +
> + /* Make sure that no worker has consumed this element. */
> + if (pg_atomic_compare_exchange_u32(&lineInfo->line_state,
> + &line_state, LINE_WORKER_PROCESSING))
> + break;
>
This is not possible because of pg_atomic_compare_exchange_u32, this
will succeed only for one of the workers whose line_state is
LINE_LEADER_POPULATED, for other workers it will fail. This is
explained in detail above ParallelCopyLineBoundary.
>
> (4) v2-0004-Documentation-for-parallel-copy.patch
>
> (i) I think that it is necessary to mention the "max_worker_processes" option in the description of the COPY statement PARALLEL option.
>
> For example, something like:
>
> + Perform <command>COPY FROM</command> in parallel using <replaceable
> + class="parameter"> integer</replaceable> background workers. Please
> + note that it is not guaranteed that the number of parallel workers
> + specified in <replaceable class="parameter">integer</replaceable> will
> + be used during execution. It is possible for a copy to run with fewer
> + workers than specified, or even with no workers at all (for example,
> + due to the setting of max_worker_processes). This option is allowed
> + only in <command>COPY FROM</command>.
>
Fixed.
> (5) v2-0005-Tests-for-parallel-copy.patch
>
> (i) None of the provided tests seem to test beyond "PARALLEL 2"
>
I intentionally ran with 1 parallel worker, because when you specify
more than 1 parallel worker the order of record insertion can vary &
there may be random failures.
>
> (6) v2-0006-Parallel-Copy-For-Binary-Format-Files.patch
>
> (i) In the ParallelCopyFrom() function, "cstate->raw_buf" is pfree()d:
>
> + /* raw_buf is not used in parallel copy, instead data blocks are used.*/
> + pfree(cstate->raw_buf);
>
raw_buf is not used in parallel copy, instead raw_buf will be pointing
to shared memory data blocks. This memory was allocated as part of
BeginCopyFrom, uptil this point we cannot be 100% sure as copy can be
performed sequentially like in case max_worker_processes is not
available, if it switches to sequential mode raw_buf will be used
while performing copy operation. At this place we can safely free this
memory that was allocated.
> This comment doesn't seem to be entirely true.
> At least for text/csv file COPY FROM, cstate->raw_buf is subsequently referenced in the SetRawBufForLoad() function, which is called by CopyReadLineText():
>
> cur_data_blk_ptr = (cstate->raw_buf) ? &pcshared_info->data_blocks[cur_block_pos] : NULL;
>
> So I think cstate->raw_buf should be set to NULL after being pfree()d, and the comment fixed/adjusted.
>
>
> (ii) This patch adds some macros (involving parallel copy checks) AFTER the comment:
>
> /* End parallel copy Macros */
Fixed, moved the macros above the comment.
I have attached new set of patches with the fixes.
Thoughts?
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
Attachments:
[text/x-patch] v3-0001-Copy-code-readjustment-to-support-parallel-copy.patch (16.7K, ../../CALDaNm3GaZyYPpGu-PpF0SEkJg-eaW3TboHxpxJ-2criv2j_eA@mail.gmail.com/2-v3-0001-Copy-code-readjustment-to-support-parallel-copy.patch)
download | inline diff:
From ddba635791eb415ffae8136b6e77875d2fac353e Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 08:52:48 +0530
Subject: [PATCH v3 1/6] Copy code readjustment to support parallel copy.
This patch has the copy code slightly readjusted so that the common code is
separated to functions/macros, these functions/macros will be used by the
workers in the parallel copy code of the upcoming patches. EOL removal is moved
from CopyReadLine to CopyReadLineText, this change was required because in case
of parallel copy the record identification and record updation is done in
CopyReadLineText, before record information is updated in shared memory the new
line characters should be removed.
---
src/backend/commands/copy.c | 361 ++++++++++++++++++++++++++------------------
1 file changed, 218 insertions(+), 143 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index db7d24a..436e458 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -95,6 +95,9 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
+
/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
@@ -224,7 +227,6 @@ typedef struct CopyStateData
* appropriate amounts of data from this buffer. In both modes, we
* guarantee that there is a \0 at raw_buf[raw_buf_len].
*/
-#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
@@ -354,6 +356,27 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/*
+ * CLEAR_EOL_LINE - Wrapper for clearing EOL.
+ */
+#define CLEAR_EOL_LINE() \
+if (!result && !IsHeaderLine()) \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len) \
+
+/*
+ * INCREMENTPROCESSED - Increment the lines processed.
+ */
+#define INCREMENTPROCESSED(processed) \
+processed++;
+
+/*
+ * RETURNPROCESSED - Get the lines processed.
+ */
+#define RETURNPROCESSED(processed) \
+return processed;
+
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -401,7 +424,11 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
-
+static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
+ List *attnamelist);
+static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size);
+static void ConvertToServerEncoding(CopyState cstate);
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -801,14 +828,18 @@ CopyLoadRawBuf(CopyState cstate)
{
int nbytes = RAW_BUF_BYTES(cstate);
int inbytes;
+ int minread = 1;
/* Copy down the unprocessed data if any. */
if (nbytes > 0)
memmove(cstate->raw_buf, cstate->raw_buf + cstate->raw_buf_index,
nbytes);
+ if (cstate->copy_dest == COPY_NEW_FE)
+ minread = RAW_BUF_SIZE - nbytes;
+
inbytes = CopyGetData(cstate, cstate->raw_buf + nbytes,
- 1, RAW_BUF_SIZE - nbytes);
+ minread, RAW_BUF_SIZE - nbytes);
nbytes += inbytes;
cstate->raw_buf[nbytes] = '\0';
cstate->raw_buf_index = 0;
@@ -1514,7 +1545,6 @@ BeginCopy(ParseState *pstate,
{
CopyState cstate;
TupleDesc tupDesc;
- int num_phys_attrs;
MemoryContext oldcontext;
/* Allocate workspace and zero all fields */
@@ -1680,6 +1710,24 @@ BeginCopy(ParseState *pstate,
tupDesc = cstate->queryDesc->tupDesc;
}
+ PopulateCommonCstateInfo(cstate, tupDesc, attnamelist);
+ cstate->copy_dest = COPY_FILE; /* default */
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return cstate;
+}
+
+/*
+ * PopulateCommonCstateInfo - Populates the common variables required for copy
+ * from operation. This is a helper function for BeginCopy function.
+ */
+static void
+PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc,
+ List *attnamelist)
+{
+ int num_phys_attrs;
+
/* Generate or convert list of attributes to process */
cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
@@ -1799,12 +1847,6 @@ BeginCopy(ParseState *pstate,
pg_database_encoding_max_length() > 1);
/* See Multibyte encoding comment above */
cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->file_encoding);
-
- cstate->copy_dest = COPY_FILE; /* default */
-
- MemoryContextSwitchTo(oldcontext);
-
- return cstate;
}
/*
@@ -2696,32 +2738,11 @@ CopyMultiInsertInfoStore(CopyMultiInsertInfo *miinfo, ResultRelInfo *rri,
}
/*
- * Copy FROM file to relation.
+ * Check if the relation specified in copy from is valid.
*/
-uint64
-CopyFrom(CopyState cstate)
+static void
+CheckTargetRelValidity(CopyState cstate)
{
- ResultRelInfo *resultRelInfo;
- ResultRelInfo *target_resultRelInfo;
- ResultRelInfo *prevResultRelInfo = NULL;
- EState *estate = CreateExecutorState(); /* for ExecConstraints() */
- ModifyTableState *mtstate;
- ExprContext *econtext;
- TupleTableSlot *singleslot = NULL;
- MemoryContext oldcontext = CurrentMemoryContext;
-
- PartitionTupleRouting *proute = NULL;
- ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
- int ti_options = 0; /* start with default options for insert */
- BulkInsertState bistate = NULL;
- CopyInsertMethod insertMethod;
- CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
- uint64 processed = 0;
- bool has_before_insert_row_trig;
- bool has_instead_insert_row_trig;
- bool leafpart_use_multi_insert = false;
-
Assert(cstate->rel);
/*
@@ -2758,27 +2779,6 @@ CopyFrom(CopyState cstate)
RelationGetRelationName(cstate->rel))));
}
- /*
- * If the target file is new-in-transaction, we assume that checking FSM
- * for free space is a waste of time. This could possibly be wrong, but
- * it's unlikely.
- */
- if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
- (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
- cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
- ti_options |= TABLE_INSERT_SKIP_FSM;
-
- /*
- * Optimize if new relfilenode was created in this subxact or one of its
- * committed children and we won't see those rows later as part of an
- * earlier scan or command. The subxact test ensures that if this subxact
- * aborts then the frozen rows won't be visible after xact cleanup. Note
- * that the stronger test of exactly which subtransaction created it is
- * crucial for correctness of this optimization. The test for an earlier
- * scan or command tolerates false negatives. FREEZE causes other sessions
- * to see rows they would not see under MVCC, and a false negative merely
- * spreads that anomaly to the current session.
- */
if (cstate->freeze)
{
/*
@@ -2816,9 +2816,61 @@ CopyFrom(CopyState cstate)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot perform COPY FREEZE because the table was not created or truncated in the current subtransaction")));
+ }
+}
+
+/*
+ * Copy FROM file to relation.
+ */
+uint64
+CopyFrom(CopyState cstate)
+{
+ ResultRelInfo *resultRelInfo;
+ ResultRelInfo *target_resultRelInfo;
+ ResultRelInfo *prevResultRelInfo = NULL;
+ EState *estate = CreateExecutorState(); /* for ExecConstraints() */
+ ModifyTableState *mtstate;
+ ExprContext *econtext;
+ TupleTableSlot *singleslot = NULL;
+ MemoryContext oldcontext = CurrentMemoryContext;
+
+ PartitionTupleRouting *proute = NULL;
+ ErrorContextCallback errcallback;
+ CommandId mycid = GetCurrentCommandId(true);
+ int ti_options = 0; /* start with default options for insert */
+ BulkInsertState bistate = NULL;
+ CopyInsertMethod insertMethod;
+ CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
+ uint64 processed = 0;
+ bool has_before_insert_row_trig;
+ bool has_instead_insert_row_trig;
+ bool leafpart_use_multi_insert = false;
+
+ CheckTargetRelValidity(cstate);
+ /*
+ * If the target file is new-in-transaction, we assume that checking FSM
+ * for free space is a waste of time. This could possibly be wrong, but
+ * it's unlikely.
+ */
+ if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
+ (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
+ cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
+ ti_options |= TABLE_INSERT_SKIP_FSM;
+
+ /*
+ * Optimize if new relfilenode was created in this subxact or one of its
+ * committed children and we won't see those rows later as part of an
+ * earlier scan or command. The subxact test ensures that if this subxact
+ * aborts then the frozen rows won't be visible after xact cleanup. Note
+ * that the stronger test of exactly which subtransaction created it is
+ * crucial for correctness of this optimization. The test for an earlier
+ * scan or command tolerates false negatives. FREEZE causes other sessions
+ * to see rows they would not see under MVCC, and a false negative merely
+ * spreads that anomaly to the current session.
+ */
+ if (cstate->freeze)
ti_options |= TABLE_INSERT_FROZEN;
- }
/*
* We need a ResultRelInfo so we can use the regular executor's
@@ -3311,7 +3363,7 @@ CopyFrom(CopyState cstate)
* or FDW; this is the same definition used by nodeModifyTable.c
* for counting tuples inserted by an INSERT command.
*/
- processed++;
+ INCREMENTPROCESSED(processed)
}
}
@@ -3366,30 +3418,15 @@ CopyFrom(CopyState cstate)
FreeExecutorState(estate);
- return processed;
+ RETURNPROCESSED(processed)
}
/*
- * Setup to read tuples from a file for COPY FROM.
- *
- * 'rel': Used as a template for the tuples
- * 'filename': Name of server-local file to read
- * 'attnamelist': List of char *, columns to include. NIL selects all cols.
- * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
- *
- * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ * PopulateCstateCatalogInfo - populate the catalog information.
*/
-CopyState
-BeginCopyFrom(ParseState *pstate,
- Relation rel,
- const char *filename,
- bool is_program,
- copy_data_source_cb data_source_cb,
- List *attnamelist,
- List *options)
+static void
+PopulateCstateCatalogInfo(CopyState cstate)
{
- CopyState cstate;
- bool pipe = (filename == NULL);
TupleDesc tupDesc;
AttrNumber num_phys_attrs,
num_defaults;
@@ -3399,38 +3436,8 @@ BeginCopyFrom(ParseState *pstate,
Oid in_func_oid;
int *defmap;
ExprState **defexprs;
- MemoryContext oldcontext;
bool volatile_defexprs;
- cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
- oldcontext = MemoryContextSwitchTo(cstate->copycontext);
-
- /* Initialize state variables */
- cstate->reached_eof = false;
- cstate->eol_type = EOL_UNKNOWN;
- cstate->cur_relname = RelationGetRelationName(cstate->rel);
- cstate->cur_lineno = 0;
- cstate->cur_attname = NULL;
- cstate->cur_attval = NULL;
-
- /*
- * Set up variables to avoid per-attribute overhead. attribute_buf and
- * raw_buf are used in both text and binary modes, but we use line_buf
- * only in text mode.
- */
- initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
- cstate->raw_buf_index = cstate->raw_buf_len = 0;
- if (!cstate->binary)
- {
- initStringInfo(&cstate->line_buf);
- cstate->line_buf_converted = false;
- }
-
- /* Assign range table, we'll need it in CopyFrom. */
- if (pstate)
- cstate->range_table = pstate->p_rtable;
-
tupDesc = RelationGetDescr(cstate->rel);
num_phys_attrs = tupDesc->natts;
num_defaults = 0;
@@ -3508,6 +3515,61 @@ BeginCopyFrom(ParseState *pstate,
cstate->defexprs = defexprs;
cstate->volatile_defexprs = volatile_defexprs;
cstate->num_defaults = num_defaults;
+}
+
+/*
+ * Setup to read tuples from a file for COPY FROM.
+ *
+ * 'rel': Used as a template for the tuples
+ * 'filename': Name of server-local file to read
+ * 'attnamelist': List of char *, columns to include. NIL selects all cols.
+ * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
+ *
+ * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ */
+CopyState
+BeginCopyFrom(ParseState *pstate,
+ Relation rel,
+ const char *filename,
+ bool is_program,
+ copy_data_source_cb data_source_cb,
+ List *attnamelist,
+ List *options)
+{
+ CopyState cstate;
+ bool pipe = (filename == NULL);
+ MemoryContext oldcontext;
+
+ cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ /* Initialize state variables */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /*
+ * Set up variables to avoid per-attribute overhead. attribute_buf is
+ * used in both text and binary modes, but we use line_buf and raw_buf
+ * only in text mode.
+ */
+ initStringInfo(&cstate->attribute_buf);
+ cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ if (!cstate->binary)
+ {
+ initStringInfo(&cstate->line_buf);
+ cstate->line_buf_converted = false;
+ }
+
+ /* Assign range table, we'll need it in CopyFrom. */
+ if (pstate)
+ cstate->range_table = pstate->p_rtable;
+
+ PopulateCstateCatalogInfo(cstate);
cstate->is_program = is_program;
if (data_source_cb)
@@ -3917,45 +3979,60 @@ CopyReadLine(CopyState cstate)
} while (CopyLoadRawBuf(cstate));
}
}
- else
- {
- /*
- * If we didn't hit EOF, then we must have transferred the EOL marker
- * to line_buf along with the data. Get rid of it.
- */
- switch (cstate->eol_type)
- {
- case EOL_NL:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CR:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\r');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CRNL:
- Assert(cstate->line_buf.len >= 2);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 2] == '\r');
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len -= 2;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_UNKNOWN:
- /* shouldn't get here */
- Assert(false);
- break;
- }
- }
+ ConvertToServerEncoding(cstate);
+ return result;
+}
+
+/*
+ * ClearEOLFromCopiedData - Clear EOL from the copied data.
+ */
+static void
+ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size)
+{
+ /*
+ * If we didn't hit EOF, then we must have transferred the EOL marker
+ * to line_buf along with the data. Get rid of it.
+ */
+ switch (cstate->eol_type)
+ {
+ case EOL_NL:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CR:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\r');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CRNL:
+ Assert(*copy_line_size >= 2);
+ Assert(copy_line_data[copy_line_pos - 2] == '\r');
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 2] = '\0';
+ *copy_line_size -= 2;
+ break;
+ case EOL_UNKNOWN:
+ /* shouldn't get here */
+ Assert(false);
+ break;
+ }
+}
+
+/*
+ * ConvertToServerEncoding - convert contents to server encoding.
+ */
+static void
+ConvertToServerEncoding(CopyState cstate)
+{
/* Done reading the line. Convert it to server encoding. */
if (cstate->need_transcoding)
{
char *cvt;
-
cvt = pg_any_to_server(cstate->line_buf.data,
cstate->line_buf.len,
cstate->file_encoding);
@@ -3967,11 +4044,8 @@ CopyReadLine(CopyState cstate)
pfree(cvt);
}
}
-
/* Now it's safe to use the buffer in error messages */
cstate->line_buf_converted = true;
-
- return result;
}
/*
@@ -4334,6 +4408,7 @@ not_end_of_copy:
* Transfer any still-uncopied data to line_buf.
*/
REFILL_LINEBUF;
+ CLEAR_EOL_LINE();
return result;
}
--
1.8.3.1
[text/x-patch] v3-0002-Framework-for-leader-worker-in-parallel-copy.patch (31.0K, ../../CALDaNm3GaZyYPpGu-PpF0SEkJg-eaW3TboHxpxJ-2criv2j_eA@mail.gmail.com/3-v3-0002-Framework-for-leader-worker-in-parallel-copy.patch)
download | inline diff:
From 7faaa1c10a793b5f0f94d35327af23b9ffe889b2 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 08:56:39 +0530
Subject: [PATCH v3 2/6] Framework for leader/worker in parallel copy
This patch has the framework for data structures in parallel copy, leader
initialization, worker initialization, shared memory updation, starting workers,
wait for workers and workers exiting.
---
src/backend/access/transam/parallel.c | 4 +
src/backend/commands/copy.c | 742 +++++++++++++++++++++++++++++++++-
src/include/commands/copy.h | 2 +
src/tools/pgindent/typedefs.list | 7 +
4 files changed, 751 insertions(+), 4 deletions(-)
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index b042696..a3cff4b 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -25,6 +25,7 @@
#include "catalog/pg_enum.h"
#include "catalog/storage.h"
#include "commands/async.h"
+#include "commands/copy.h"
#include "executor/execParallel.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
@@ -145,6 +146,9 @@ static const struct
},
{
"parallel_vacuum_main", parallel_vacuum_main
+ },
+ {
+ "ParallelCopyMain", ParallelCopyMain
}
};
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 436e458..25ef664 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -96,9 +96,180 @@ typedef enum CopyInsertMethod
} CopyInsertMethod;
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+
+/*
+ * The macros DATA_BLOCK_SIZE, RINGSIZE & MAX_BLOCKS_COUNT stores the records
+ * read from the file that need to be inserted into the relation. These values
+ * help in handover of multiple records with significant size of data to be
+ * processed by each of the workers to make sure there is no context switch & the
+ * work is fairly distributed among the workers. This number showed best
+ * results in the performance tests.
+ */
+#define DATA_BLOCK_SIZE RAW_BUF_SIZE
+
+/* It can hold upto 10000 record information for worker to process. */
+#define RINGSIZE (10 * 1000)
+
+/* It can hold 1000 blocks of 64K data in DSM to be processed by the worker. */
+#define MAX_BLOCKS_COUNT 1000
+
+/*
+ * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
+ * block to process to avoid lock contention. This value should be divisible by
+ * RINGSIZE, as wrap around cases is currently not handled while selecting the
+ * WORKER_CHUNK_COUNT by the worker.
+ */
+#define WORKER_CHUNK_COUNT 50
+
+#define IsParallelCopy() (cstate->is_parallel)
+#define IsLeader() (cstate->pcdata->is_leader)
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
+ * Copy data block information.
+ * ParallelCopyDataBlock's will be created in DSM. Data read from file will be
+ * copied in these DSM data blocks. The leader process identifies the records
+ * and the record information will be shared to the workers. The workers will
+ * insert the records into the table. There can be one or more number of records
+ * in each of the data block based on the record size.
+ */
+typedef struct ParallelCopyDataBlock
+{
+ /* The number of unprocessed lines in the current block. */
+ pg_atomic_uint32 unprocessed_line_parts;
+
+ /*
+ * If the current line data is continued into another block, following_block
+ * will have the position where the remaining data need to be read.
+ */
+ uint32 following_block;
+
+ /*
+ * This flag will be set, when the leader finds out this block can be read
+ * safely by the worker. This helps the worker to start processing the line
+ * early where the line will be spread across many blocks and the worker
+ * need not wait for the complete line to be processed.
+ */
+ bool curr_blk_completed;
+ char data[DATA_BLOCK_SIZE]; /* data read from file */
+ uint8 skip_bytes;
+}ParallelCopyDataBlock;
+
+/*
+ * Individual line information.
+ * ParallelCopyLineBoundary is common data structure between leader & worker,
+ * Leader process will be populating data block, data block offset & the size of
+ * the record in DSM for the workers to copy the data into the relation.
+ * This is protected by the following sequence in the leader & worker. If they
+ * don't follow this order the worker might process wrong line_size and leader
+ * might populate the information which worker has not yet processed or in the
+ * process of processing.
+ * Leader should operate in the following order:
+ * 1) check if line_size is -1, if not wait, it means worker is still
+ * processing.
+ * 2) set line_state to LINE_LEADER_POPULATING.
+ * 3) update first_block, start_offset & cur_lineno in any order.
+ * 4) update line_size.
+ * 5) update line_state to LINE_LEADER_POPULATED.
+ * Worker should operate in the following order:
+ * 1) check line_state is LINE_LEADER_POPULATED, if not it means leader is still
+ * populating the data.
+ * 2) read line_size.
+ * 3) only one worker should choose one line for processing, this is handled by
+ * using pg_atomic_compare_exchange_u32, worker will change the state to
+ * LINE_WORKER_PROCESSING only if line_state is LINE_LEADER_POPULATED.
+ * 4) read first_block, start_offset & cur_lineno in any order.
+ * 5) process line_size data.
+ * 6) update line_size to -1.
+ */
+typedef struct ParallelCopyLineBoundary
+{
+ /* Position of the first block in data_blocks array. */
+ uint32 first_block;
+ uint32 start_offset; /* start offset of the line */
+
+ /*
+ * Size of the current line -1 means line is yet to be filled completely,
+ * 0 means empty line, >0 means line filled with line size data.
+ */
+ pg_atomic_uint32 line_size;
+ pg_atomic_uint32 line_state; /* line state */
+ uint64 cur_lineno; /* line number for error messages */
+}ParallelCopyLineBoundary;
+
+/*
+ * Circular queue used to store the line information.
+ */
+typedef struct ParallelCopyLineBoundaries
+{
+ /* Position for the leader to populate a line. */
+ uint32 pos;
+
+ /* Data read from the file/stdin by the leader process. */
+ ParallelCopyLineBoundary ring[RINGSIZE];
+}ParallelCopyLineBoundaries;
+
+/*
+ * Shared information among parallel copy workers. This will be allocated in the
+ * DSM segment.
+ */
+typedef struct ParallelCopyShmInfo
+{
+ bool is_read_in_progress; /* file read status */
+
+ /*
+ * Actual lines inserted by worker, will not be same as
+ * total_worker_processed if where condition is specified along with copy.
+ * This will be the actual records inserted into the relation.
+ */
+ pg_atomic_uint64 processed;
+
+ /*
+ * The number of records currently processed by the worker, this will also
+ * include the number of records that was filtered because of where clause.
+ */
+ pg_atomic_uint64 total_worker_processed;
+ uint64 populated; /* lines populated by leader */
+ uint32 cur_block_pos; /* current data block */
+ ParallelCopyDataBlock data_blocks[MAX_BLOCKS_COUNT]; /* data block array */
+ FullTransactionId full_transaction_id; /* xid for copy from statement */
+ CommandId mycid; /* command id */
+ ParallelCopyLineBoundaries line_boundaries; /* line array */
+} ParallelCopyShmInfo;
+
+/*
+ * Parallel copy line buffer information.
+ */
+typedef struct ParallelCopyLineBuf
+{
+ StringInfoData line_buf;
+ uint64 cur_lineno; /* line number for error messages */
+}ParallelCopyLineBuf;
+
+/*
+ * Parallel copy data information.
+ */
+typedef struct ParallelCopyData
+{
+ Oid relid; /* relation id of the table */
+ ParallelCopyShmInfo *pcshared_info; /* common info in shared memory */
+ bool is_leader;
+
+ /* line position which worker is processing */
+ uint32 worker_processed_pos;
+
+ /*
+ * Local line_buf array, workers will copy it here and release the lines
+ * for the leader to continue.
+ */
+ ParallelCopyLineBuf worker_line_buf[WORKER_CHUNK_COUNT];
+ uint32 worker_line_buf_count; /* Number of lines */
+
+ /* Current position in worker_line_buf */
+ uint32 worker_line_buf_pos;
+}ParallelCopyData;
+
+/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
* even though some fields are used in only some cases.
@@ -230,10 +401,38 @@ typedef struct CopyStateData
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
+ int nworkers;
+ bool is_parallel;
+ ParallelCopyData *pcdata;
/* Shorthand for number of unconsumed bytes available in raw_buf */
#define RAW_BUF_BYTES(cstate) ((cstate)->raw_buf_len - (cstate)->raw_buf_index)
} CopyStateData;
+/*
+ * This structure helps in storing the common data from CopyStateData that are
+ * required by the workers. This information will then be allocated and stored
+ * into the DSM for the worker to retrieve and copy it to CopyStateData.
+ */
+typedef struct SerializedParallelCopyState
+{
+ /* low-level state data */
+ CopyDest copy_dest; /* type of copy source/destination */
+ int file_encoding; /* file or remote side's character encoding */
+ bool need_transcoding; /* file encoding diff from server? */
+ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */
+
+ /* parameters from the COPY command */
+ bool csv_mode; /* Comma Separated Value format? */
+ bool header_line; /* CSV header line? */
+ int null_print_len; /* length of same */
+ bool force_quote_all; /* FORCE_QUOTE *? */
+ bool convert_selectively; /* do selective binary conversion? */
+
+ /* Working state for COPY FROM */
+ AttrNumber num_defaults;
+ Oid relid;
+}SerializedParallelCopyState;
+
/* DestReceiver for COPY (query) TO */
typedef struct
{
@@ -263,6 +462,22 @@ typedef struct
/* Trim the list of buffers back down to this number after flushing */
#define MAX_PARTITION_BUFFERS 32
+/* DSM keys for parallel copy. */
+#define PARALLEL_COPY_KEY_SHARED_INFO 1
+#define PARALLEL_COPY_KEY_CSTATE 2
+#define PARALLEL_COPY_KEY_NULL_PRINT 3
+#define PARALLEL_COPY_KEY_DELIM 4
+#define PARALLEL_COPY_KEY_QUOTE 5
+#define PARALLEL_COPY_KEY_ESCAPE 6
+#define PARALLEL_COPY_KEY_ATTNAME_LIST 7
+#define PARALLEL_COPY_KEY_NOT_NULL_LIST 8
+#define PARALLEL_COPY_KEY_NULL_LIST 9
+#define PARALLEL_COPY_KEY_CONVERT_LIST 10
+#define PARALLEL_COPY_KEY_WHERE_CLAUSE_STR 11
+#define PARALLEL_COPY_KEY_RANGE_TABLE 12
+#define PARALLEL_COPY_WAL_USAGE 13
+#define PARALLEL_COPY_BUFFER_USAGE 14
+
/* Stores multi-insert data related to a single relation in CopyFrom. */
typedef struct CopyMultiInsertBuffer
{
@@ -424,11 +639,477 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
+static pg_attribute_always_inline void EndParallelCopy(ParallelContext *pcxt);
static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
- List *attnamelist);
+ List *attnamelist);
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
+
+/*
+ * SerializeParallelCopyState - Copy shared_cstate using cstate information.
+ */
+static pg_attribute_always_inline void
+SerializeParallelCopyState(CopyState cstate, SerializedParallelCopyState *shared_cstate)
+{
+ shared_cstate->copy_dest = cstate->copy_dest;
+ shared_cstate->file_encoding = cstate->file_encoding;
+ shared_cstate->need_transcoding = cstate->need_transcoding;
+ shared_cstate->encoding_embeds_ascii = cstate->encoding_embeds_ascii;
+ shared_cstate->csv_mode = cstate->csv_mode;
+ shared_cstate->header_line = cstate->header_line;
+ shared_cstate->null_print_len = cstate->null_print_len;
+ shared_cstate->force_quote_all = cstate->force_quote_all;
+ shared_cstate->convert_selectively = cstate->convert_selectively;
+ shared_cstate->num_defaults = cstate->num_defaults;
+ shared_cstate->relid = cstate->pcdata->relid;
+}
+
+/*
+ * RestoreString - Retrieve the string from shared memory.
+ */
+static void
+RestoreString(shm_toc *toc, int sharedkey, char **copystr)
+{
+ char *shared_str_val = (char *)shm_toc_lookup(toc, sharedkey, true);
+ if (shared_str_val)
+ *copystr = pstrdup(shared_str_val);
+}
+
+/*
+ * EstimateLineKeysStr - Estimate the size required in shared memory for the
+ * input string.
+ */
+static void
+EstimateLineKeysStr(ParallelContext *pcxt, char *inputstr)
+{
+ if (inputstr)
+ {
+ shm_toc_estimate_chunk(&pcxt->estimator, strlen(inputstr) + 1);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ }
+}
+
+/*
+ * SerializeString - Insert a string into shared memory.
+ */
+static void
+SerializeString(ParallelContext *pcxt, int key, char *inputstr)
+{
+ if (inputstr)
+ {
+ char *shmptr = (char *)shm_toc_allocate(pcxt->toc,
+ strlen(inputstr) + 1);
+ strcpy(shmptr, inputstr);
+ shm_toc_insert(pcxt->toc, key, shmptr);
+ }
+}
+
+/*
+ * PopulateParallelCopyShmInfo - set ParallelCopyShmInfo.
+ */
+static void
+PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
+ FullTransactionId full_transaction_id)
+{
+ uint32 count;
+
+ MemSet(shared_info_ptr, 0, sizeof(ParallelCopyShmInfo));
+ shared_info_ptr->is_read_in_progress = true;
+ shared_info_ptr->cur_block_pos = -1;
+ shared_info_ptr->full_transaction_id = full_transaction_id;
+ shared_info_ptr->mycid = GetCurrentCommandId(true);
+ for (count = 0; count < RINGSIZE; count++)
+ {
+ ParallelCopyLineBoundary *lineInfo = &shared_info_ptr->line_boundaries.ring[count];
+ pg_atomic_init_u32(&(lineInfo->line_size), -1);
+ }
+}
+
+/*
+ * BeginParallelCopy - start parallel copy tasks.
+ *
+ * Get the number of workers required to perform the parallel copy. The data
+ * structures that are required by the parallel workers will be initialized, the
+ * size required in DSM will be calculated and the necessary keys will be loaded
+ * in the DSM. The specified number of workers will then be launched.
+ */
+static ParallelContext*
+BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
+{
+ ParallelContext *pcxt;
+ ParallelCopyShmInfo *shared_info_ptr;
+ SerializedParallelCopyState *shared_cstate;
+ FullTransactionId full_transaction_id;
+ Size est_cstateshared;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ int parallel_workers = 0;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+ ParallelCopyData *pcdata;
+ MemoryContext oldcontext;
+
+ parallel_workers = Min(nworkers, max_worker_processes);
+
+ /* Can't perform copy in parallel */
+ if (parallel_workers <= 0)
+ return NULL;
+
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ MemoryContextSwitchTo(oldcontext);
+ cstate->pcdata = pcdata;
+
+ EnterParallelMode();
+ pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
+ parallel_workers);
+ Assert(pcxt->nworkers > 0);
+
+ /*
+ * Estimate size for shared information for PARALLEL_COPY_KEY_SHARED_INFO
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(ParallelCopyShmInfo));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_CSTATE */
+ est_cstateshared = MAXALIGN(sizeof(SerializedParallelCopyState));
+ shm_toc_estimate_chunk(&pcxt->estimator, est_cstateshared);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ EstimateLineKeysStr(pcxt, cstate->null_print);
+ EstimateLineKeysStr(pcxt, cstate->null_print_client);
+ EstimateLineKeysStr(pcxt, cstate->delim);
+ EstimateLineKeysStr(pcxt, cstate->quote);
+ EstimateLineKeysStr(pcxt, cstate->escape);
+
+ if (cstate->whereClause != NULL)
+ {
+ whereClauseStr = nodeToString(cstate->whereClause);
+ EstimateLineKeysStr(pcxt, whereClauseStr);
+ }
+
+ if (cstate->range_table != NULL)
+ {
+ rangeTableStr = nodeToString(cstate->range_table);
+ EstimateLineKeysStr(pcxt, rangeTableStr);
+ }
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_XID. */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(FullTransactionId));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_ATTNAME_LIST.
+ */
+ if (attnamelist != NIL)
+ {
+ attnameListStr = nodeToString(attnamelist);
+ EstimateLineKeysStr(pcxt, attnameListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NOT_NULL_LIST.
+ */
+ if (cstate->force_notnull != NIL)
+ {
+ notnullListStr = nodeToString(cstate->force_notnull);
+ EstimateLineKeysStr(pcxt, notnullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NULL_LIST.
+ */
+ if (cstate->force_null != NIL)
+ {
+ nullListStr = nodeToString(cstate->force_null);
+ EstimateLineKeysStr(pcxt, nullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_CONVERT_LIST.
+ */
+ if (cstate->convert_select != NIL)
+ {
+ convertListStr = nodeToString(cstate->convert_select);
+ EstimateLineKeysStr(pcxt, convertListStr);
+ }
+
+ /*
+ * Estimate space for WalUsage and BufferUsage -- PARALLEL_COPY_WAL_USAGE
+ * and PARALLEL_COPY_BUFFER_USAGE.
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ InitializeParallelDSM(pcxt);
+
+ /* If no DSM segment was available, back out (do serial copy) */
+ if (pcxt->seg == NULL)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /* Allocate shared memory for PARALLEL_COPY_KEY_SHARED_INFO */
+ shared_info_ptr = (ParallelCopyShmInfo *) shm_toc_allocate(pcxt->toc, sizeof(ParallelCopyShmInfo));
+ PopulateParallelCopyShmInfo(shared_info_ptr, full_transaction_id);
+
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_SHARED_INFO, shared_info_ptr);
+ pcdata->pcshared_info = shared_info_ptr;
+ pcdata->relid = relid;
+
+ /* Store shared build state, for which we reserved space. */
+ shared_cstate = (SerializedParallelCopyState *)shm_toc_allocate(pcxt->toc, est_cstateshared);
+
+ /* copy cstate variables. */
+ SerializeParallelCopyState(cstate, shared_cstate);
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_CSTATE, shared_cstate);
+
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ATTNAME_LIST, attnameListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NOT_NULL_LIST, notnullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_LIST, nullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_CONVERT_LIST, convertListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, whereClauseStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_RANGE_TABLE, rangeTableStr);
+
+ /*
+ * Allocate space for each worker's WalUsage and BufferUsage; no need to
+ * initialize.
+ */
+ walusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_WAL_USAGE, walusage);
+ bufferusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_BUFFER_USAGE, bufferusage);
+
+ LaunchParallelWorkers(pcxt);
+ if (pcxt->nworkers_launched == 0)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /*
+ * Caller needs to wait for all launched workers when we return. Make sure
+ * that the failure-to-start case will not hang forever.
+ */
+ WaitForParallelWorkersToAttach(pcxt);
+
+ pcdata->is_leader = true;
+ cstate->is_parallel = true;
+ return pcxt;
+}
+
+/*
+ * EndParallelCopy - end the parallel copy tasks.
+ */
+static pg_attribute_always_inline void
+EndParallelCopy(ParallelContext *pcxt)
+{
+ Assert(!IsParallelWorker());
+
+ DestroyParallelContext(pcxt);
+ ExitParallelMode();
+}
+
+/*
+ * InitializeParallelCopyInfo - Initialize parallel worker.
+ */
+static void
+InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
+ CopyState cstate, List *attnamelist)
+{
+ uint32 count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ cstate->copy_dest = shared_cstate->copy_dest;
+ cstate->file_encoding = shared_cstate->file_encoding;
+ cstate->need_transcoding = shared_cstate->need_transcoding;
+ cstate->encoding_embeds_ascii = shared_cstate->encoding_embeds_ascii;
+ cstate->csv_mode = shared_cstate->csv_mode;
+ cstate->header_line = shared_cstate->header_line;
+ cstate->null_print_len = shared_cstate->null_print_len;
+ cstate->force_quote_all = shared_cstate->force_quote_all;
+ cstate->convert_selectively = shared_cstate->convert_selectively;
+ cstate->num_defaults = shared_cstate->num_defaults;
+ pcdata->relid = shared_cstate->relid;
+
+ PopulateCommonCstateInfo(cstate, tup_desc, attnamelist);
+
+ /* Initialize state variables. */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /* Set up variables to avoid per-attribute overhead. */
+ initStringInfo(&cstate->attribute_buf);
+
+ initStringInfo(&cstate->line_buf);
+ for (count = 0; count < WORKER_CHUNK_COUNT; count++)
+ initStringInfo(&pcdata->worker_line_buf[count].line_buf);
+
+ cstate->line_buf_converted = false;
+ cstate->raw_buf = NULL;
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+}
+
+/*
+ * ParallelCopyMain - parallel copy worker's code.
+ *
+ * Where clause handling, convert tuple to columns, add default null values for
+ * the missing columns that are not present in that record. Find the partition
+ * if it is partitioned table, invoke before row insert Triggers, handle
+ * constraints and insert the tuples.
+ */
+void
+ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
+{
+ CopyState cstate;
+ ParallelCopyData *pcdata;
+ ParallelCopyShmInfo *pcshared_info;
+ SerializedParallelCopyState *shared_cstate;
+ Relation rel = NULL;
+ MemoryContext oldcontext;
+ List *attlist = NIL;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+
+ /* Allocate workspace and zero all fields. */
+ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData));
+
+ /*
+ * We allocate everything used by a cstate in a new memory context. This
+ * avoids memory leaks during repeated use of COPY in a query.
+ */
+ cstate->copycontext = AllocSetContextCreate(CurrentMemoryContext,
+ "COPY",
+ ALLOCSET_DEFAULT_SIZES);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ cstate->pcdata = pcdata;
+ pcdata->is_leader = false;
+ pcdata->worker_processed_pos = -1;
+ cstate->is_parallel = true;
+ pcshared_info = (ParallelCopyShmInfo *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_SHARED_INFO, false);
+
+ ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
+
+ pcdata->pcshared_info = pcshared_info;
+
+ shared_cstate = (SerializedParallelCopyState *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
+ cstate->null_print = (char *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_DELIM, &cstate->delim);
+ RestoreString(toc, PARALLEL_COPY_KEY_QUOTE, &cstate->quote);
+ RestoreString(toc, PARALLEL_COPY_KEY_ESCAPE, &cstate->escape);
+ RestoreString(toc, PARALLEL_COPY_KEY_ATTNAME_LIST, &attnameListStr);
+ if (attnameListStr)
+ attlist = (List *)stringToNode(attnameListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NOT_NULL_LIST, ¬nullListStr);
+ if (notnullListStr)
+ cstate->force_notnull = (List *)stringToNode(notnullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NULL_LIST, &nullListStr);
+ if (nullListStr)
+ cstate->force_null = (List *)stringToNode(nullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_CONVERT_LIST, &convertListStr);
+ if (convertListStr)
+ cstate->convert_select = (List *)stringToNode(convertListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, &whereClauseStr);
+ RestoreString(toc, PARALLEL_COPY_KEY_RANGE_TABLE, &rangeTableStr);
+
+ if (whereClauseStr)
+ {
+ Node *whereClauseCnvrtdFrmStr = (Node *) stringToNode(whereClauseStr);
+ cstate->whereClause = whereClauseCnvrtdFrmStr;
+ }
+
+ if (rangeTableStr)
+ {
+ List *rangeTableCnvrtdFrmStr = (List *) stringToNode(rangeTableStr);
+ cstate->range_table = rangeTableCnvrtdFrmStr;
+ }
+
+ /* Open and lock the relation, using the appropriate lock type. */
+ rel = table_open(shared_cstate->relid, RowExclusiveLock);
+ cstate->rel = rel;
+ InitializeParallelCopyInfo(shared_cstate, cstate, attlist);
+
+ CopyFrom(cstate);
+
+ if (rel != NULL)
+ table_close(rel, RowExclusiveLock);
+
+ /* Report WAL/buffer usage during parallel execution */
+ bufferusage = shm_toc_lookup(toc, PARALLEL_COPY_BUFFER_USAGE, false);
+ walusage = shm_toc_lookup(toc, PARALLEL_COPY_WAL_USAGE, false);
+ InstrEndParallelQuery(&bufferusage[ParallelWorkerNumber],
+ &walusage[ParallelWorkerNumber]);
+
+ MemoryContextSwitchTo(oldcontext);
+ pfree(cstate);
+ return;
+}
+
+/*
+ * ParallelCopyFrom - parallel copy leader's functionality.
+ *
+ * Leader executes the before statement for before statement trigger, if before
+ * statement trigger is present. It will read the table data from the file and
+ * copy the contents to DSM data blocks. It will then read the input contents
+ * from the DSM data block and identify the records based on line breaks. This
+ * information is called line or a record that need to be inserted into a
+ * relation. The line information will be stored in ParallelCopyLineBoundary DSM
+ * data structure. Workers will then process this information and insert the
+ * data in to table. It will repeat this process until the all data is read from
+ * the file and all the DSM data blocks are processed. While processing if
+ * leader identifies that DSM Data blocks or DSM ParallelCopyLineBoundary data
+ * structures is full, leader will wait till the worker frees up some entries
+ * and repeat the process. It will wait till all the lines populated are
+ * processed by the workers and exits.
+ */
+static void
+ParallelCopyFrom(CopyState cstate)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+
+ pcshared_info->is_read_in_progress = false;
+ cstate->cur_lineno = 0;
+}
+
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -1141,6 +1822,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
if (is_from)
{
+ ParallelContext *pcxt = NULL;
Assert(rel);
/* check read-only transaction and parallel mode */
@@ -1150,7 +1832,24 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);
cstate->whereClause = whereClause;
- *processed = CopyFrom(cstate); /* copy from file to database */
+ cstate->is_parallel = false;
+
+ if (cstate->nworkers > 0)
+ pcxt = BeginParallelCopy(cstate->nworkers, cstate, stmt->attlist,
+ relid);
+
+ if (pcxt)
+ {
+ ParallelCopyFrom(cstate);
+
+ /* Wait for all copy workers to finish */
+ WaitForParallelWorkersToFinish(pcxt);
+ *processed = pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+ EndParallelCopy(pcxt);
+ }
+ else
+ *processed = CopyFrom(cstate); /* copy from file to database */
+
EndCopyFrom(cstate);
}
else
@@ -1199,6 +1898,7 @@ ProcessCopyOptions(ParseState *pstate,
cstate->is_copy_from = is_from;
cstate->file_encoding = -1;
+ cstate->nworkers = -1;
/* Extract options from the statement node tree */
foreach(option, options)
@@ -1367,6 +2067,39 @@ ProcessCopyOptions(ParseState *pstate,
defel->defname),
parser_errposition(pstate, defel->location)));
}
+ else if (strcmp(defel->defname, "parallel") == 0)
+ {
+ int val;
+ bool parsed;
+ char *strval;
+
+ if (!cstate->is_copy_from)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("parallel option is supported only for copy from"),
+ parser_errposition(pstate, defel->location)));
+ if (cstate->nworkers >= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options"),
+ parser_errposition(pstate, defel->location)));
+
+ strval = defGetString(defel);
+ parsed = parse_int(strval, &val, 0, NULL);
+ if (!parsed)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for integer option \"%s\": %s",
+ defel->defname, strval)));
+ if (val < 1 || val > 1024)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value %s out of bounds for option \"%s\"",
+ strval, defel->defname),
+ errdetail("Valid values are between \"%d\" and \"%d\".",
+ 1, 1024)));
+ cstate->nworkers = val;
+ }
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1720,11 +2453,12 @@ BeginCopy(ParseState *pstate,
/*
* PopulateCommonCstateInfo - Populates the common variables required for copy
- * from operation. This is a helper function for BeginCopy function.
+ * from operation. This is a helper function for BeginCopy &
+ * InitializeParallelCopyInfo function.
*/
static void
PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc,
- List *attnamelist)
+ List *attnamelist)
{
int num_phys_attrs;
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index c639833..82843c6 100644
--- a/src/include/commands/copy.h
+++ b/src/include/commands/copy.h
@@ -14,6 +14,7 @@
#ifndef COPY_H
#define COPY_H
+#include "access/parallel.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
#include "parser/parse_node.h"
@@ -41,4 +42,5 @@ extern uint64 CopyFrom(CopyState cstate);
extern DestReceiver *CreateCopyDestReceiver(void);
+extern void ParallelCopyMain(dsm_segment *seg, shm_toc *toc);
#endif /* COPY_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b4948ac..bb49b65 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1701,6 +1701,12 @@ ParallelBitmapHeapState
ParallelBlockTableScanDesc
ParallelCompletionPtr
ParallelContext
+ParallelCopyLineBoundaries
+ParallelCopyLineBoundary
+ParallelCopyData
+ParallelCopyDataBlock
+ParallelCopyLineBuf
+ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
ParallelHashJoinBatch
@@ -2217,6 +2223,7 @@ SerCommitSeqNo
SerialControl
SerializableXactHandle
SerializedActiveRelMaps
+SerializedParallelCopyState
SerializedReindexState
SerializedSnapshotData
SerializedTransactionState
--
1.8.3.1
[text/x-patch] v3-0003-Allow-copy-from-command-to-process-data-from-file.patch (43.0K, ../../CALDaNm3GaZyYPpGu-PpF0SEkJg-eaW3TboHxpxJ-2criv2j_eA@mail.gmail.com/4-v3-0003-Allow-copy-from-command-to-process-data-from-file.patch)
download | inline diff:
From c944b276b8f8b92a98d63d4a66ff931baa8fab1b Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:31:42 +0530
Subject: [PATCH v3 3/6] Allow copy from command to process data from
file/STDIN contents to a table in parallel.
This feature allows the copy from to leverage multiple CPUs in order to copy
data from file/STDIN to a table. This adds a PARALLEL option to COPY FROM
command where the user can specify the number of workers that can be used
to perform the COPY FROM command.
The backend, to which the "COPY FROM" query is submitted acts as leader with
the responsibility of reading data from the file/stdin, launching at most n
number of workers as specified with PARALLEL 'n' option in the "COPY FROM"
query. The leader populates the common data required for the workers execution
in the DSM and shares it with the workers. The leader then executes before
statement triggers if there exists any. Leader populates DSM lines which
includes the start offset and line size, while populating the lines it reads
as many blocks as required into the DSM data blocks from the file. Each block
is of 64K size. The leader parses the data to identify a line, the existing
logic from CopyReadLineText which identifies the lines with some changes was
used for this. Leader checks if a free line is available to copy the
information, if there is no free line it waits till the required line is
freed up by the worker and then copies the identified lines information
(offset & line size) into the DSM lines. This process is repeated till the
complete file is processed. Simultaneously, the workers cache the lines(50)
locally into the local memory and release the lines to the leader for further
populating. Each worker processes the lines that it cached and inserts it into
the table. The leader does not participate in the insertion of data, leaders
only responsibility will be to identify the lines as fast as possible for the
workers to do the actual copy operation. The leader waits till all the lines
populated are processed by the workers and exits.
We have chosen this design based on the reason "that everything stalls if the
leader doesn't accept further input data, as well as when there are no
available splitted chunks so it doesn't seem like a good idea to have the
leader do other work. This is backed by the performance data where we have
seen that with 1 worker there is just a 5-10% performance difference".
---
src/backend/access/common/toast_internals.c | 11 +-
src/backend/access/heap/heapam.c | 13 -
src/backend/access/transam/xact.c | 31 +
src/backend/commands/copy.c | 892 ++++++++++++++++++++++++++--
src/bin/psql/tab-complete.c | 2 +-
src/include/access/xact.h | 2 +
src/tools/pgindent/typedefs.list | 1 +
7 files changed, 898 insertions(+), 54 deletions(-)
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 25a81e5..586d53d 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -17,6 +17,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heaptoast.h"
+#include "access/parallel.h"
#include "access/table.h"
#include "access/toast_internals.h"
#include "access/xact.h"
@@ -116,7 +117,15 @@ toast_save_datum(Relation rel, Datum value,
TupleDesc toasttupDesc;
Datum t_values[3];
bool t_isnull[3];
- CommandId mycid = GetCurrentCommandId(true);
+
+ /*
+ * Parallel copy can insert toast tuples, in case of parallel copy the
+ * command would have been set already by calling AssignCommandIdForWorker.
+ * For parallel copy call GetCurrentCommandId to get currentCommandId by
+ * passing used as false, as this is taken care earlier.
+ */
+ CommandId mycid = IsParallelWorker() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
struct varlena *result;
struct varatt_external toast_pointer;
union
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index f75e1cf..fa005df 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2043,19 +2043,6 @@ static HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
- /*
- * Parallel operations are required to be strictly read-only in a parallel
- * worker. Parallel inserts are not safe even in the leader in the
- * general case, because group locking means that heavyweight locks for
- * relation extension or GIN page locks will not conflict between members
- * of a lock group, but we don't prohibit that case here because there are
- * useful special cases that we can safely allow, such as CREATE TABLE AS.
- */
- if (IsParallelWorker())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot insert tuples in a parallel worker")));
-
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 7ccb7d6..e2bcac2 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -517,6 +517,37 @@ GetCurrentFullTransactionIdIfAny(void)
}
/*
+ * AssignFullTransactionIdForWorker
+ *
+ * For parallel copy, transaction id of leader will be used by the workers.
+ */
+void
+AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId)
+{
+ TransactionState s = CurrentTransactionState;
+
+ Assert((IsInParallelMode() || IsParallelWorker()));
+ s->fullTransactionId = fullTransactionId;
+}
+
+/*
+ * AssignCommandIdForWorker
+ *
+ * For parallel copy, command id of leader will be used by the workers.
+ */
+void
+AssignCommandIdForWorker(CommandId commandId, bool used)
+{
+ Assert((IsInParallelMode() || IsParallelWorker()));
+
+ /* this is global to a transaction, not subtransaction-local */
+ if (used)
+ currentCommandIdUsed = true;
+
+ currentCommandId = commandId;
+}
+
+/*
* MarkCurrentTransactionIdLoggedIfAny
*
* Remember that the current xid - if it is assigned - now has been wal logged.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 25ef664..4c69a5d 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -26,6 +26,7 @@
#include "access/xlog.h"
#include "catalog/dependency.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_proc_d.h"
#include "catalog/pg_type.h"
#include "commands/copy.h"
#include "commands/defrem.h"
@@ -40,11 +41,13 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
+#include "pgstat.h"
#include "port/pg_bswap.h"
#include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
@@ -95,6 +98,18 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+/*
+ * State of the line.
+ */
+typedef enum ParallelCopyLineState
+{
+ LINE_INIT, /* initial state of line */
+ LINE_LEADER_POPULATING, /* leader processing line */
+ LINE_LEADER_POPULATED, /* leader completed populating line */
+ LINE_WORKER_PROCESSING, /* worker processing line */
+ LINE_WORKER_PROCESSED /* worker completed processing line */
+}ParallelCopyLineState;
+
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
/*
@@ -123,6 +138,7 @@ typedef enum CopyInsertMethod
#define IsParallelCopy() (cstate->is_parallel)
#define IsLeader() (cstate->pcdata->is_leader)
+#define IsWorker() (IsParallelCopy() && !IsLeader())
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
@@ -556,9 +572,13 @@ if (1) \
{ \
if (raw_buf_ptr > cstate->raw_buf_index) \
{ \
- appendBinaryStringInfo(&cstate->line_buf, \
- cstate->raw_buf + cstate->raw_buf_index, \
- raw_buf_ptr - cstate->raw_buf_index); \
+ if (!IsParallelCopy()) \
+ appendBinaryStringInfo(&cstate->line_buf, \
+ cstate->raw_buf + cstate->raw_buf_index, \
+ raw_buf_ptr - cstate->raw_buf_index); \
+ else \
+ line_size += raw_buf_ptr - cstate->raw_buf_index; \
+ \
cstate->raw_buf_index = raw_buf_ptr; \
} \
} else ((void) 0)
@@ -571,26 +591,65 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/* Begin parallel copy Macros */
+#define SET_NEWLINE_SIZE() \
+{ \
+ if (cstate->eol_type == EOL_NL || cstate->eol_type == EOL_CR) \
+ new_line_size = 1; \
+ else if (cstate->eol_type == EOL_CRNL) \
+ new_line_size = 2; \
+ else \
+ new_line_size = 0; \
+}
+
+/*
+ * COPY_WAIT_TO_PROCESS - Wait before continuing to process.
+ */
+#define COPY_WAIT_TO_PROCESS() \
+{ \
+ CHECK_FOR_INTERRUPTS(); \
+ (void) WaitLatch(MyLatch, \
+ WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, \
+ 1L, WAIT_EVENT_PG_SLEEP); \
+ ResetLatch(MyLatch); \
+}
+
/*
* CLEAR_EOL_LINE - Wrapper for clearing EOL.
*/
#define CLEAR_EOL_LINE() \
if (!result && !IsHeaderLine()) \
- ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
- cstate->line_buf.len, \
- &cstate->line_buf.len) \
+{ \
+ if (IsParallelCopy()) \
+ ClearEOLFromCopiedData(cstate, cstate->raw_buf, \
+ raw_buf_ptr, &line_size); \
+ else \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len); \
+} \
/*
* INCREMENTPROCESSED - Increment the lines processed.
*/
-#define INCREMENTPROCESSED(processed) \
-processed++;
+#define INCREMENTPROCESSED(processed) \
+{ \
+ if (!IsParallelCopy()) \
+ processed++; \
+ else \
+ pg_atomic_add_fetch_u64(&cstate->pcdata->pcshared_info->processed, 1); \
+}
/*
* RETURNPROCESSED - Get the lines processed.
*/
#define RETURNPROCESSED(processed) \
-return processed;
+if (!IsParallelCopy()) \
+ return processed; \
+else \
+ return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+
+/* End parallel copy Macros */
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -645,7 +704,10 @@ static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
-
+static void ExecBeforeStmtTrigger(CopyState cstate);
+static void CheckTargetRelValidity(CopyState cstate);
+static void PopulateCstateCatalogInfo(CopyState cstate);
+static pg_attribute_always_inline uint32 GetLinePosition(CopyState cstate);
/*
* SerializeParallelCopyState - Copy shared_cstate using cstate information.
*/
@@ -727,6 +789,137 @@ PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
}
/*
+ * IsTriggerFunctionParallelSafe - Check if the trigger function is parallel
+ * safe for the triggers. Return false if any one of the trigger has parallel
+ * unsafe function.
+ */
+static pg_attribute_always_inline bool
+IsTriggerFunctionParallelSafe(TriggerDesc *trigdesc)
+{
+ int i;
+ for (i = 0; i < trigdesc->numtriggers; i++)
+ {
+ Trigger *trigger = &trigdesc->triggers[i];
+ int trigtype = RI_TRIGGER_NONE;
+
+ if (func_parallel(trigger->tgfoid) != PROPARALLEL_SAFE)
+ return false;
+
+ /* If the trigger is parallel safe, also look for RI_TRIGGER. */
+ trigtype = RI_FKey_trigger_type(trigger->tgfoid);
+ if (trigtype == RI_TRIGGER_PK || trigtype == RI_TRIGGER_FK)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * CheckExprParallelSafety - determine parallel safety of volatile expressions
+ * in default clause of column definition or in where clause and return true if
+ * they are parallel safe.
+ */
+static pg_attribute_always_inline bool
+CheckExprParallelSafety(CopyState cstate)
+{
+ if (contain_volatile_functions(cstate->whereClause))
+ {
+ if (max_parallel_hazard((Query *)cstate->whereClause) != PROPARALLEL_SAFE)
+ return false;
+ }
+
+ /*
+ * Check if any of the column has volatile default expression. if yes, and
+ * they are not parallel safe, then parallelism is not allowed. For
+ * instance, if there are any serial/bigserial columns for which nextval()
+ * default expression which is parallel unsafe is associated, parallelism
+ * should not be allowed. In non parallel copy volatile functions are not
+ * checked for nextval().
+ */
+ if (cstate->defexprs != NULL && cstate->num_defaults != 0)
+ {
+ int i;
+ for (i = 0; i < cstate->num_defaults; i++)
+ {
+ bool volatile_expr = contain_volatile_functions((Node *)cstate->defexprs[i]->expr);
+ if (volatile_expr &&
+ (max_parallel_hazard((Query *)cstate->defexprs[i]->expr)) !=
+ PROPARALLEL_SAFE)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * FindInsertMethod - determine insert mode single, multi, or multi conditional.
+ */
+static pg_attribute_always_inline CopyInsertMethod
+FindInsertMethod(CopyState cstate)
+{
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_before_row ||
+ cstate->rel->trigdesc->trig_insert_instead_row))
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+ cstate->rel->trigdesc != NULL &&
+ cstate->rel->trigdesc->trig_insert_new_table)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ return CIM_MULTI_CONDITIONAL;
+
+ return CIM_MULTI;
+}
+
+/*
+ * IsParallelCopyAllowed - check for the cases where parallel copy is not
+ * applicable.
+ */
+static pg_attribute_always_inline bool
+IsParallelCopyAllowed(CopyState cstate)
+{
+ /* Parallel copy not allowed for frontend (2.0 protocol) & binary option. */
+ if ((cstate->copy_dest == COPY_OLD_FE) || cstate->binary)
+ return false;
+
+ /* Check if copy is into foreign table or temporary table. */
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
+ RelationUsesLocalBuffers(cstate->rel))
+ return false;
+
+ /* Check if trigger function is parallel safe. */
+ if (cstate->rel->trigdesc != NULL &&
+ !IsTriggerFunctionParallelSafe(cstate->rel->trigdesc))
+ return false;
+
+ /*
+ * Check if there is after statement or instead of trigger or transition
+ * table triggers.
+ */
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_after_statement ||
+ cstate->rel->trigdesc->trig_insert_instead_row ||
+ cstate->rel->trigdesc->trig_insert_new_table))
+ return false;
+
+ /* Check if the volatile expressions are parallel safe, if present any. */
+ if (!CheckExprParallelSafety(cstate))
+ return false;
+
+ /* Check if the insertion mode is single. */
+ if (FindInsertMethod(cstate) == CIM_SINGLE)
+ return false;
+
+ return true;
+}
+
+/*
* BeginParallelCopy - start parallel copy tasks.
*
* Get the number of workers required to perform the parallel copy. The data
@@ -754,6 +947,7 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
ParallelCopyData *pcdata;
MemoryContext oldcontext;
+ CheckTargetRelValidity(cstate);
parallel_workers = Min(nworkers, max_worker_processes);
/* Can't perform copy in parallel */
@@ -765,6 +959,15 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
MemoryContextSwitchTo(oldcontext);
cstate->pcdata = pcdata;
+ /*
+ * User chosen parallel copy. Determine if the parallel copy is actually
+ * allowed. If not, go with the non-parallel mode.
+ */
+ if (!IsParallelCopyAllowed(cstate))
+ return NULL;
+
+ full_transaction_id = GetCurrentFullTransactionId();
+
EnterParallelMode();
pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
parallel_workers);
@@ -973,9 +1176,214 @@ InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
cstate->line_buf_converted = false;
cstate->raw_buf = NULL;
cstate->raw_buf_index = cstate->raw_buf_len = 0;
+
+ PopulateCstateCatalogInfo(cstate);
+
+ /* Create workspace for CopyReadAttributes results. */
+ if (!cstate->binary)
+ {
+ AttrNumber attr_count = list_length(cstate->attnumlist);
+
+ cstate->max_fields = attr_count;
+ cstate->raw_fields = (char **)palloc(attr_count * sizeof(char *));
+ }
}
/*
+ * CacheLineInfo - Cache the line information to local memory.
+ */
+static bool
+CacheLineInfo(CopyState cstate, uint32 buff_count)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ uint32 write_pos;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 offset;
+ int dataSize;
+ int copiedSize = 0;
+
+ resetStringInfo(&pcdata->worker_line_buf[buff_count].line_buf);
+ write_pos = GetLinePosition(cstate);
+ if (-1 == write_pos)
+ return true;
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ if (pg_atomic_read_u32(&lineInfo->line_size) == 0)
+ goto empty_data_line_update;
+
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ /* Get the offset information from where the data must be copied. */
+ offset = lineInfo->start_offset;
+ pcdata->worker_line_buf[buff_count].cur_lineno = lineInfo->cur_lineno;
+
+ elog(DEBUG1, "[Worker] Processing - line position:%d, block:%d, unprocessed lines:%d, offset:%d, line size:%d",
+ write_pos, lineInfo->first_block,
+ pg_atomic_read_u32(&data_blk_ptr->unprocessed_line_parts),
+ offset, pg_atomic_read_u32(&lineInfo->line_size));
+
+ for (;;)
+ {
+ uint8 skip_bytes = data_blk_ptr->skip_bytes;
+
+ /*
+ * There is a possibility that the loop embedded at the bottom of the
+ * current loop has come out because data_blk_ptr->curr_blk_completed
+ * is set, but dataSize read might be an old value, if
+ * data_blk_ptr->curr_blk_completed and the line is completed, line_size
+ * will be set. Read the line_size again to be sure if it is completed
+ * or partial block.
+ */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+ if (dataSize)
+ {
+ int remainingSize = dataSize - copiedSize;
+ if (!remainingSize)
+ break;
+
+ /* Whole line is in current block. */
+ if (remainingSize + offset + skip_bytes < DATA_BLOCK_SIZE)
+ {
+ appendBinaryStringInfo(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ remainingSize);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts,
+ 1);
+ break;
+ }
+ else
+ {
+ /* Line is spread across the blocks. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+ while (copiedSize < dataSize)
+ {
+ uint32 currentBlockCopySize;
+ ParallelCopyDataBlock *currBlkPtr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ skip_bytes = currBlkPtr->skip_bytes;
+
+ /*
+ * If complete data is present in current block use
+ * dataSize - copiedSize, or copy the whole block from
+ * current block.
+ */
+ currentBlockCopySize = Min(dataSize - copiedSize, DATA_BLOCK_SIZE - skip_bytes);
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &currBlkPtr->data[0],
+ currentBlockCopySize);
+ pg_atomic_sub_fetch_u32(&currBlkPtr->unprocessed_line_parts, 1);
+ copiedSize += currentBlockCopySize;
+ data_blk_ptr = currBlkPtr;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ /* Copy this complete block from the current offset. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+
+ /*
+ * Reset the offset. For the first copy, copy from the offset. For
+ * the subsequent copy the complete block.
+ */
+ offset = 0;
+
+ /* Set data_blk_ptr to the following block. */
+ data_blk_ptr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ }
+
+ for (;;)
+ {
+ /* Get the size of this line */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ /*
+ * If the data is present in current block lineInfo. line_size
+ * will be updated. If the data is spread across the blocks either
+ * of lineInfo.line_size or data_blk_ptr->curr_blk_completed can
+ * be updated. lineInfo.line_size will be updated if the complete
+ * read is finished. data_blk_ptr->curr_blk_completed will be
+ * updated if processing of current block is finished and data
+ * processing is not finished.
+ */
+ if (data_blk_ptr->curr_blk_completed || (dataSize != -1))
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+ }
+
+empty_data_line_update:
+ elog(DEBUG1, "[Worker] Completed processing line:%d", write_pos);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_WORKER_PROCESSED);
+ pg_atomic_write_u32(&lineInfo->line_size, -1);
+ pg_atomic_add_fetch_u64(&pcshared_info->total_worker_processed, 1);
+ return false;
+}
+
+/*
+ * GetWorkerLine - Returns a line for worker to process.
+ */
+static bool
+GetWorkerLine(CopyState cstate)
+{
+ uint32 buff_count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+
+ /*
+ * Copy the line data to line_buf and release the line position so that the
+ * worker can continue loading data.
+ */
+ if (pcdata->worker_line_buf_pos < pcdata->worker_line_buf_count)
+ goto return_line;
+
+ pcdata->worker_line_buf_pos = 0;
+ pcdata->worker_line_buf_count = 0;
+
+ for (buff_count = 0; buff_count < WORKER_CHUNK_COUNT; buff_count++)
+ {
+ bool result = CacheLineInfo(cstate, buff_count);
+ if (result)
+ break;
+
+ pcdata->worker_line_buf_count++;
+ }
+
+ if (pcdata->worker_line_buf_count)
+ goto return_line;
+ else
+ resetStringInfo(&cstate->line_buf);
+
+ return true;
+
+return_line:
+ cstate->line_buf = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].line_buf;
+ cstate->cur_lineno = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].cur_lineno;
+ cstate->line_buf_valid = true;
+
+ /* Mark that encoding conversion hasn't occurred yet. */
+ cstate->line_buf_converted = false;
+ ConvertToServerEncoding(cstate);
+ pcdata->worker_line_buf_pos++;
+ return false;
+ }
+
+/*
* ParallelCopyMain - parallel copy worker's code.
*
* Where clause handling, convert tuple to columns, add default null values for
@@ -1024,6 +1432,8 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
pcdata->pcshared_info = pcshared_info;
+ AssignFullTransactionIdForWorker(pcshared_info->full_transaction_id);
+ AssignCommandIdForWorker(pcshared_info->mycid, true);
shared_cstate = (SerializedParallelCopyState *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
cstate->null_print = (char *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
@@ -1084,6 +1494,33 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
}
/*
+ * UpdateBlockInLineInfo - Update the line information.
+ */
+static pg_attribute_always_inline int
+UpdateBlockInLineInfo(CopyState cstate, uint32 blk_pos,
+ uint32 offset, uint32 line_size, uint32 line_state)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ ParallelCopyLineBoundary *lineInfo;
+ int line_pos = lineBoundaryPtr->pos;
+
+ /* Update the line information for the worker to pick and process. */
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ while (pg_atomic_read_u32(&lineInfo->line_size) != -1)
+ COPY_WAIT_TO_PROCESS()
+
+ lineInfo->first_block = blk_pos;
+ lineInfo->start_offset = offset;
+ lineInfo->cur_lineno = cstate->cur_lineno;
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, line_state);
+ lineBoundaryPtr->pos = (lineBoundaryPtr->pos + 1) % RINGSIZE;
+
+ return line_pos;
+}
+
+/*
* ParallelCopyFrom - parallel copy leader's functionality.
*
* Leader executes the before statement for before statement trigger, if before
@@ -1106,8 +1543,298 @@ ParallelCopyFrom(CopyState cstate)
ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+ /* Execute the before statement triggers from the leader */
+ ExecBeforeStmtTrigger(cstate);
+
+ /* On input just throw the header line away. */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ {
+ pcshared_info->is_read_in_progress = false;
+ return; /* done */
+ }
+ }
+
+ for (;;)
+ {
+ bool done;
+ cstate->cur_lineno++;
+
+ /* Actually read the line into memory here. */
+ done = CopyReadLine(cstate);
+
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ break;
+ }
+
pcshared_info->is_read_in_progress = false;
cstate->cur_lineno = 0;
+ }
+
+/*
+ * GetLinePosition - return the line position that worker should process.
+ */
+static uint32
+GetLinePosition(CopyState cstate)
+{
+ ParallelCopyData *pcdata = cstate->pcdata;
+ ParallelCopyShmInfo *pcshared_info = pcdata->pcshared_info;
+ uint32 previous_pos = pcdata->worker_processed_pos;
+ uint32 write_pos = (previous_pos == -1) ? 0 : (previous_pos + 1) % RINGSIZE;
+ for (;;)
+ {
+ int dataSize;
+ bool is_read_in_progress = pcshared_info->is_read_in_progress;
+ ParallelCopyLineBoundary *lineInfo;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineState line_state = LINE_LEADER_POPULATED;
+ ParallelCopyLineState curr_line_state;
+ CHECK_FOR_INTERRUPTS();
+
+ /* File read completed & no elements to process. */
+ if (!is_read_in_progress &&
+ (pcshared_info->populated ==
+ pg_atomic_read_u64(&pcshared_info->total_worker_processed)))
+ {
+ write_pos = -1;
+ break;
+ }
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ curr_line_state = pg_atomic_read_u32(&lineInfo->line_state);
+ if ((write_pos % WORKER_CHUNK_COUNT == 0) &&
+ (curr_line_state == LINE_WORKER_PROCESSED ||
+ curr_line_state == LINE_WORKER_PROCESSING))
+ {
+ pcdata->worker_processed_pos = write_pos;
+ write_pos = (write_pos + WORKER_CHUNK_COUNT) % RINGSIZE;
+ continue;
+ }
+
+ /* Get the size of this line. */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ if (dataSize != 0) /* If not an empty line. */
+ {
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ if (!data_blk_ptr->curr_blk_completed && (dataSize == -1))
+ {
+ /* Wait till the current line or block is added. */
+ COPY_WAIT_TO_PROCESS()
+ continue;
+ }
+ }
+
+ /* Make sure that no worker has consumed this element. */
+ if (pg_atomic_compare_exchange_u32(&lineInfo->line_state,
+ &line_state, LINE_WORKER_PROCESSING))
+ break;
+ }
+
+ pcdata->worker_processed_pos = write_pos;
+ return write_pos;
+}
+
+/*
+ * GetFreeCopyBlock - Get a free block for data to be copied.
+ */
+static pg_attribute_always_inline uint32
+GetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ int count = 0;
+ uint32 last_free_block = pcshared_info->cur_block_pos;
+ uint32 block_pos = (last_free_block != -1) ? ((last_free_block + 1) % MAX_BLOCKS_COUNT): 0;
+
+ /*
+ * Get a new block for copying data, don't check current block, current
+ * block will have some unprocessed data.
+ */
+ while (count < (MAX_BLOCKS_COUNT - 1))
+ {
+ ParallelCopyDataBlock *dataBlkPtr = &pcshared_info->data_blocks[block_pos];
+ uint32 unprocessed_line_parts = pg_atomic_read_u32(&dataBlkPtr->unprocessed_line_parts);
+ if (unprocessed_line_parts == 0)
+ {
+ dataBlkPtr->curr_blk_completed = false;
+ dataBlkPtr->skip_bytes = 0;
+ pcshared_info->cur_block_pos = block_pos;
+ return block_pos;
+ }
+
+ block_pos = (block_pos + 1) % MAX_BLOCKS_COUNT;
+ count++;
+ }
+
+ return -1;
+}
+
+/*
+ * WaitGetFreeCopyBlock - If there are no blocks available, wait and get a block
+ * for copying data.
+ */
+static uint32
+WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ uint32 new_free_pos = -1;
+ for (;;)
+ {
+ new_free_pos = GetFreeCopyBlock(pcshared_info);
+ if (new_free_pos != -1) /* We have got one block, break now. */
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+
+ return new_free_pos;
+}
+
+/*
+ * SetRawBufForLoad - Set raw_buf to the shared memory where the file data must
+ * be read.
+ */
+static void
+SetRawBufForLoad(CopyState cstate, uint32 line_size, uint32 copy_buf_len,
+ uint32 raw_buf_ptr, char **copy_raw_buf)
+{
+ ParallelCopyShmInfo *pcshared_info;
+ uint32 cur_block_pos;
+ uint32 next_block_pos;
+ ParallelCopyDataBlock *cur_data_blk_ptr = NULL;
+ ParallelCopyDataBlock *next_data_blk_ptr = NULL;
+
+ if (!IsParallelCopy())
+ return;
+
+ pcshared_info = cstate->pcdata->pcshared_info;
+ cur_block_pos = pcshared_info->cur_block_pos;
+ cur_data_blk_ptr = (cstate->raw_buf) ? &pcshared_info->data_blocks[cur_block_pos] : NULL;
+ next_block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ next_data_blk_ptr = &pcshared_info->data_blocks[next_block_pos];
+
+ /* set raw_buf to the data block in shared memory */
+ cstate->raw_buf = next_data_blk_ptr->data;
+ *copy_raw_buf = cstate->raw_buf;
+ if (cur_data_blk_ptr && line_size)
+ {
+ /*
+ * Mark the previous block as completed, worker can start copying this
+ * data.
+ */
+ cur_data_blk_ptr->following_block = next_block_pos;
+ pg_atomic_add_fetch_u32(&cur_data_blk_ptr->unprocessed_line_parts, 1);
+ cur_data_blk_ptr->skip_bytes = copy_buf_len - raw_buf_ptr;
+ cur_data_blk_ptr->curr_blk_completed = true;
+ }
+}
+
+/*
+ * EndLineParallelCopy - Update the line information in shared memory.
+ */
+static void
+EndLineParallelCopy(CopyState cstate, uint32 line_pos, uint32 line_size,
+ uint32 raw_buf_ptr)
+{
+ uint8 new_line_size;
+ if (!IsParallelCopy())
+ return;
+
+ if (!IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ SET_NEWLINE_SIZE()
+ if (line_size)
+ {
+ ParallelCopyLineBoundary *lineInfo = &lineBoundaryPtr->ring[line_pos];
+ /*
+ * If the new_line_size > raw_buf_ptr, then the new block has only
+ * new line char content. The unprocessed count should not be
+ * increased in this case.
+ */
+ if (raw_buf_ptr > new_line_size)
+ {
+ uint32 cur_block_pos = pcshared_info->cur_block_pos;
+ ParallelCopyDataBlock *curr_data_blk_ptr = &pcshared_info->data_blocks[cur_block_pos];
+ pg_atomic_add_fetch_u32(&curr_data_blk_ptr->unprocessed_line_parts, 1);
+ }
+
+ /* Update line size. */
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_LEADER_POPULATED);
+ elog(DEBUG1, "[Leader] After adding - line position:%d, line_size:%d",
+ line_pos, line_size);
+ pcshared_info->populated++;
+ }
+ else if (new_line_size)
+ {
+ /* This means only new line char, empty record should be inserted.*/
+ ParallelCopyLineBoundary *lineInfo;
+ line_pos = UpdateBlockInLineInfo(cstate, -1, -1, 0,
+ LINE_LEADER_POPULATED);
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ elog(DEBUG1, "[Leader] Added empty line with offset:%d, line position:%d, line size:%d",
+ lineInfo->start_offset, line_pos,
+ pg_atomic_read_u32(&lineInfo->line_size));
+ pcshared_info->populated++;
+ }
+ }
+}
+
+/*
+ * ExecBeforeStmtTrigger - Execute the before statement trigger, this will be
+ * executed for parallel copy by the leader process.
+ */
+static void
+ExecBeforeStmtTrigger(CopyState cstate)
+{
+ EState *estate = CreateExecutorState();
+ ResultRelInfo *resultRelInfo;
+
+ Assert(IsLeader());
+
+ /*
+ * We need a ResultRelInfo so we can use the regular executor's
+ * index-entry-making machinery. (There used to be a huge amount of code
+ * here that basically duplicated execUtils.c ...)
+ */
+ resultRelInfo = makeNode(ResultRelInfo);
+ InitResultRelInfo(resultRelInfo,
+ cstate->rel,
+ 1, /* must match rel's position in range_table */
+ NULL,
+ 0);
+
+ /* Verify the named relation is a valid target for INSERT */
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
+
+ estate->es_result_relations = resultRelInfo;
+ estate->es_num_result_relations = 1;
+ estate->es_result_relation_info = resultRelInfo;
+
+ ExecInitRangeTable(estate, cstate->range_table);
+
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+
+ /* Close any trigger target relations */
+ ExecCleanUpTriggerState(estate);
+
+ FreeExecutorState(estate);
}
/*
@@ -3570,7 +4297,8 @@ CopyFrom(CopyState cstate)
PartitionTupleRouting *proute = NULL;
ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
+ CommandId mycid = IsParallelCopy() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
int ti_options = 0; /* start with default options for insert */
BulkInsertState bistate = NULL;
CopyInsertMethod insertMethod;
@@ -3580,7 +4308,14 @@ CopyFrom(CopyState cstate)
bool has_instead_insert_row_trig;
bool leafpart_use_multi_insert = false;
- CheckTargetRelValidity(cstate);
+ /*
+ * Perform this check if it is not parallel copy. In case of parallel
+ * copy, this check is done by the leader, so that if any invalid case
+ * exist the copy from command will error out from the leader itself,
+ * avoiding launching workers, just to throw error.
+ */
+ if (!IsParallelCopy())
+ CheckTargetRelValidity(cstate);
/*
* If the target file is new-in-transaction, we assume that checking FSM
@@ -3620,7 +4355,8 @@ CopyFrom(CopyState cstate)
target_resultRelInfo = resultRelInfo;
/* Verify the named relation is a valid target for INSERT */
- CheckValidResultRel(resultRelInfo, CMD_INSERT);
+ if (!IsParallelCopy())
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
ExecOpenIndices(resultRelInfo, false);
@@ -3769,13 +4505,16 @@ CopyFrom(CopyState cstate)
has_instead_insert_row_trig = (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_instead_row);
- /*
- * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
- * should do this for COPY, since it's not really an "INSERT" statement as
- * such. However, executing these triggers maintains consistency with the
- * EACH ROW triggers that we already fire on COPY.
- */
- ExecBSInsertTriggers(estate, resultRelInfo);
+ if (!IsParallelCopy())
+ {
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+ }
econtext = GetPerTupleExprContext(estate);
@@ -3875,6 +4614,16 @@ CopyFrom(CopyState cstate)
!has_instead_insert_row_trig &&
resultRelInfo->ri_FdwRoutine == NULL;
+ /*
+ * If the table has any partitions that are either foreign or
+ * has BEFORE/INSTEAD OF triggers, we can't perform copy
+ * operations with parallel workers.
+ */
+ if (!leafpart_use_multi_insert && IsParallelWorker())
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot perform PARALLEL COPY if partition has BEFORE/INSTEAD OF triggers, or if the partition is foreign partition"),
+ errhint("Try COPY without PARALLEL option")));
+
/* Set the multi-insert buffer to use for this partition. */
if (leafpart_use_multi_insert)
{
@@ -4291,7 +5040,7 @@ BeginCopyFrom(ParseState *pstate,
* only in text mode.
*/
initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf = (IsParallelCopy()) ? NULL : (char *) palloc(RAW_BUF_SIZE + 1);
cstate->raw_buf_index = cstate->raw_buf_len = 0;
if (!cstate->binary)
{
@@ -4440,26 +5189,35 @@ NextCopyFromRawFields(CopyState cstate, char ***fields, int *nfields)
/* only available for text or csv input */
Assert(!cstate->binary);
- /* on input just throw the header line away */
- if (cstate->cur_lineno == 0 && cstate->header_line)
+ if (IsParallelCopy())
{
- cstate->cur_lineno++;
- if (CopyReadLine(cstate))
- return false; /* done */
+ done = GetWorkerLine(cstate);
+ if (done && cstate->line_buf.len == 0)
+ return false;
}
+ else
+ {
+ /* on input just throw the header line away */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ return false; /* done */
+ }
- cstate->cur_lineno++;
+ cstate->cur_lineno++;
- /* Actually read the line into memory here */
- done = CopyReadLine(cstate);
+ /* Actually read the line into memory here */
+ done = CopyReadLine(cstate);
- /*
- * EOF at start of line means we're done. If we see EOF after some
- * characters, we act as though it was newline followed by EOF, ie,
- * process the line and then exit loop on next iteration.
- */
- if (done && cstate->line_buf.len == 0)
- return false;
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ return false;
+ }
/* Parse the line into de-escaped field values */
if (cstate->csv_mode)
@@ -4707,9 +5465,31 @@ CopyReadLine(CopyState cstate)
*/
if (cstate->copy_dest == COPY_NEW_FE)
{
+ bool bIsFirst = true;
do
{
- cstate->raw_buf_index = cstate->raw_buf_len;
+ if (!IsParallelCopy())
+ cstate->raw_buf_index = cstate->raw_buf_len;
+ else
+ {
+ if (cstate->raw_buf_index == RAW_BUF_SIZE)
+ {
+ /* Get a new block if it is the first time, From the
+ * subsequent time, reset the index and re-use the same
+ * block.
+ */
+ if (bIsFirst)
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ uint32 block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ cstate->raw_buf = pcshared_info->data_blocks[block_pos].data;
+ bIsFirst = false;
+ }
+
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ }
+ }
+
} while (CopyLoadRawBuf(cstate));
}
}
@@ -4764,7 +5544,7 @@ static void
ConvertToServerEncoding(CopyState cstate)
{
/* Done reading the line. Convert it to server encoding. */
- if (cstate->need_transcoding)
+ if (cstate->need_transcoding && (!IsParallelCopy() || IsWorker()))
{
char *cvt;
cvt = pg_any_to_server(cstate->line_buf.data,
@@ -4803,6 +5583,11 @@ CopyReadLineText(CopyState cstate)
char quotec = '\0';
char escapec = '\0';
+ /* For parallel copy */
+ int line_size = 0;
+ int line_pos = 0;
+
+ cstate->eol_type = EOL_UNKNOWN;
if (cstate->csv_mode)
{
quotec = cstate->quote[0];
@@ -4857,6 +5642,8 @@ CopyReadLineText(CopyState cstate)
if (raw_buf_ptr >= copy_buf_len || need_data)
{
REFILL_LINEBUF;
+ SetRawBufForLoad(cstate, line_size, copy_buf_len, raw_buf_ptr,
+ ©_raw_buf);
/*
* Try to read some more data. This will certainly reset
@@ -5081,9 +5868,15 @@ CopyReadLineText(CopyState cstate)
* discard the data and the \. sequence.
*/
if (prev_raw_ptr > cstate->raw_buf_index)
- appendBinaryStringInfo(&cstate->line_buf,
+ {
+ if (!IsParallelCopy())
+ appendBinaryStringInfo(&cstate->line_buf,
cstate->raw_buf + cstate->raw_buf_index,
prev_raw_ptr - cstate->raw_buf_index);
+ else
+ line_size += prev_raw_ptr - cstate->raw_buf_index;
+ }
+
cstate->raw_buf_index = raw_buf_ptr;
result = true; /* report EOF */
break;
@@ -5135,6 +5928,26 @@ not_end_of_copy:
IF_NEED_REFILL_AND_EOF_BREAK(mblen - 1);
raw_buf_ptr += mblen - 1;
}
+
+ /*
+ * Skip the header line. Update the line here, this cannot be done at
+ * the beginning, as there is a possibility that file contains empty
+ * lines.
+ */
+ if (IsParallelCopy() && first_char_in_line && !IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 line_first_block = pcshared_info->cur_block_pos;
+ line_pos = UpdateBlockInLineInfo(cstate,
+ line_first_block,
+ cstate->raw_buf_index, -1,
+ LINE_LEADER_POPULATING);
+ lineInfo = &pcshared_info->line_boundaries.ring[line_pos];
+ elog(DEBUG1, "[Leader] Adding - block:%d, offset:%d, line position:%d",
+ line_first_block, lineInfo->start_offset, line_pos);
+ }
+
first_char_in_line = false;
} /* end of outer loop */
@@ -5143,6 +5956,7 @@ not_end_of_copy:
*/
REFILL_LINEBUF;
CLEAR_EOL_LINE();
+ EndLineParallelCopy(cstate, line_pos, line_size, raw_buf_ptr);
return result;
}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index f41785f..81cc1f4 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2353,7 +2353,7 @@ psql_completion(const char *text, int start, int end)
/* Complete COPY <sth> FROM|TO filename WITH ( */
else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "("))
COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
- "HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
+ "HEADER", "PARALLEL", "QUOTE", "ESCAPE", "FORCE_QUOTE",
"FORCE_NOT_NULL", "FORCE_NULL", "ENCODING");
/* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index c18554b..94219e8 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -385,6 +385,8 @@ extern FullTransactionId GetTopFullTransactionId(void);
extern FullTransactionId GetTopFullTransactionIdIfAny(void);
extern FullTransactionId GetCurrentFullTransactionId(void);
extern FullTransactionId GetCurrentFullTransactionIdIfAny(void);
+extern void AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId);
+extern void AssignCommandIdForWorker(CommandId commandId, bool used);
extern void MarkCurrentTransactionIdLoggedIfAny(void);
extern bool SubTransactionIsActive(SubTransactionId subxid);
extern CommandId GetCurrentCommandId(bool used);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index bb49b65..120c7a6 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1706,6 +1706,7 @@ ParallelCopyLineBoundary
ParallelCopyData
ParallelCopyDataBlock
ParallelCopyLineBuf
+ParallelCopyLineState
ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
--
1.8.3.1
[text/x-patch] v3-0004-Documentation-for-parallel-copy.patch (2.0K, ../../CALDaNm3GaZyYPpGu-PpF0SEkJg-eaW3TboHxpxJ-2criv2j_eA@mail.gmail.com/5-v3-0004-Documentation-for-parallel-copy.patch)
download | inline diff:
From 8969d183e86199c9035d47459217d5e46f51c49f Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:00:36 +0530
Subject: [PATCH v3 4/6] Documentation for parallel copy.
This patch has the documentation changes for parallel copy.
---
doc/src/sgml/ref/copy.sgml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index 18189ab..2e023ed 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -37,6 +37,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
DELIMITER '<replaceable class="parameter">delimiter_character</replaceable>'
NULL '<replaceable class="parameter">null_string</replaceable>'
HEADER [ <replaceable class="parameter">boolean</replaceable> ]
+ PARALLEL <replaceable class="parameter">integer</replaceable>
QUOTE '<replaceable class="parameter">quote_character</replaceable>'
ESCAPE '<replaceable class="parameter">escape_character</replaceable>'
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
@@ -275,6 +276,22 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
</varlistentry>
<varlistentry>
+ <term><literal>PARALLEL</literal></term>
+ <listitem>
+ <para>
+ Perform <command>COPY FROM</command> in parallel using <replaceable
+ class="parameter"> integer</replaceable> background workers. Please
+ note that it is not guaranteed that the number of parallel workers
+ specified in <replaceable class="parameter">integer</replaceable> will
+ be used during execution. It is possible for a copy to run with fewer
+ workers than specified, or even with no workers at all (for example,
+ due to the setting of max_worker_processes). This option is allowed
+ only in <command>COPY FROM</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>QUOTE</literal></term>
<listitem>
<para>
--
1.8.3.1
[text/x-patch] v3-0005-Tests-for-parallel-copy.patch (19.7K, ../../CALDaNm3GaZyYPpGu-PpF0SEkJg-eaW3TboHxpxJ-2criv2j_eA@mail.gmail.com/6-v3-0005-Tests-for-parallel-copy.patch)
download | inline diff:
From d72e976bf5310679f2cbac8d91205fea169abbef Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:19:39 +0530
Subject: [PATCH v3 5/6] Tests for parallel copy.
This patch has the tests for parallel copy.
---
src/test/regress/expected/copy2.out | 205 ++++++++++++++++++++++++++++++++++-
src/test/regress/input/copy.source | 12 +++
src/test/regress/output/copy.source | 12 +++
src/test/regress/sql/copy2.sql | 208 +++++++++++++++++++++++++++++++++++-
4 files changed, 429 insertions(+), 8 deletions(-)
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index e40287d..7ae5d44 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -254,18 +254,32 @@ It is "perfect".|
"It is ""perfect""."," "
"",
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+SELECT * FROM testnl;
+ a | b | c
+---+----------------------+---
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+(2 rows)
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
COPY testeoc TO stdout CSV;
a\.
\.b
c\.d
"\."
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
1 \\0
@@ -280,6 +294,15 @@ SELECT * FROM testnull;
|
(4 rows)
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+SELECT * FROM testnull;
+ a | b
+----+----
+ 42 | \0
+ |
+(2 rows)
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -349,6 +372,34 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ a2
+ b
+(2 rows)
+
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+COMMIT;
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
SELECT * FROM vistest;
a
@@ -409,7 +460,7 @@ SELECT * FROM vistest;
(2 rows)
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -420,6 +471,8 @@ CREATE TEMP TABLE forcetest (
-- should succeed with no effect ("b" remains an empty string, "c" remains NULL)
BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
b | c
@@ -430,6 +483,8 @@ SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
c | d
@@ -486,6 +541,31 @@ select * from check_con_tbl;
(2 rows)
+\d+ check_con_tbl
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | | | plain | |
+Check constraints:
+ "check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
+
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":1}
+NOTICE: input = {"f1":null}
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":0}
+ERROR: new row for relation "check_con_tbl" violates check constraint "check_con_tbl_check"
+DETAIL: Failing row contains (0).
+CONTEXT: COPY check_con_tbl, line 1: "0"
+parallel worker
+select * from check_con_tbl;
+ f1
+----
+ 1
+
+(2 rows)
+
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
CREATE ROLE regress_rls_copy_user_colperms;
@@ -600,8 +680,125 @@ SELECT * FROM instead_of_insert_tbl;
(2 rows)
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+ERROR: value 0 out of bounds for option "parallel"
+DETAIL: Valid values are between "1" and "1024".
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+COPY temp_test (a) from stdin with (PARALLEL 1);
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+ERROR: column "xyz" of relation "test_parallel_copy" does not exist
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+ERROR: column "d" specified more than once
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: invalid input syntax for type integer: ""
+CONTEXT: COPY test_parallel_copy, line 0, column a: ""
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2000 230 23 23"
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2001 231 \N \N"
+parallel worker
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: extra data after last expected column
+CONTEXT: COPY test_parallel_copy, line 1: "2002 232 40 50 60 70 80"
+parallel worker
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ERROR: column "f" does not exist
+LINE 1: ..._parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ERROR: missing FROM-clause entry for table "x"
+LINE 1: ...rallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+ERROR: cannot use subquery in COPY FROM WHERE condition
+LINE 1: ...arallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT...
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+ERROR: set-returning functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...lel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_s...
+ ^
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+ERROR: window functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...rallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number...
+ ^
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+ a | b | c | d | e
+-------+----+------------+---------+---------
+ 1 | 11 | test_c1 | test_d1 | test_e1
+ 2 | 12 | test_c2 | test_d2 | test_e2
+ | 3 | stuff | test_d3 |
+ | 4 | stuff | test_d4 |
+ | 5 | stuff | test_d5 |
+ | | 45 | 80 | 90
+ | | x | \x | \x
+ | | , | \, | \
+ 3000 | | c | |
+ 4000 | | C | |
+ 4001 | 1 | empty | |
+ 4002 | 2 | null | |
+ 4003 | 3 | Backslash | \ | \
+ 4004 | 4 | BackslashX | \X | \X
+ 4005 | 5 | N | N | N
+ 4006 | 6 | BackslashN | \N | \N
+ 4007 | 7 | XX | XX | XX
+ 4008 | 8 | Delimiter | : | :
+ 50004 | 25 | 35 | 45 | 55
+ 60004 | 25 | 35 | 45 | 55
+ 60005 | 26 | 36 | 46 | 56
+(21 rows)
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source
index a1d529a..159c058 100644
--- a/src/test/regress/input/copy.source
+++ b/src/test/regress/input/copy.source
@@ -15,6 +15,13 @@ DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+
+SELECT COUNT(*) FROM tenk1;
+
+TRUNCATE tenk1;
+
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
@@ -159,6 +166,11 @@ truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
+
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source
index 938d355..c3003fe 100644
--- a/src/test/regress/output/copy.source
+++ b/src/test/regress/output/copy.source
@@ -9,6 +9,15 @@ COPY onek FROM '@abs_srcdir@/data/onek.data';
COPY onek TO '@abs_builddir@/results/onek.data';
DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+SELECT COUNT(*) FROM tenk1;
+ count
+-------
+ 10000
+(1 row)
+
+TRUNCATE tenk1;
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
COPY person FROM '@abs_srcdir@/data/person.data';
@@ -113,6 +122,9 @@ insert into parted_copytest select x,1,'One' from generate_series(1011,1020) x;
copy (select * from parted_copytest order by a) to '@abs_builddir@/results/parted_copytest.csv';
truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql
index 902f4fa..7015698 100644
--- a/src/test/regress/sql/copy2.sql
+++ b/src/test/regress/sql/copy2.sql
@@ -157,7 +157,7 @@ COPY y TO stdout (FORMAT CSV, FORCE_QUOTE *);
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
1,"a field with two LFs
@@ -165,8 +165,16 @@ COPY testnl FROM stdin CSV;
inside",2
\.
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+1,"a field with two LFs
+
+inside",2
+\.
+
+SELECT * FROM testnl;
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
a\.
@@ -175,11 +183,19 @@ c\.d
"\."
\.
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+a\.
+\.b
+c\.d
+"\."
+\.
+
COPY testeoc TO stdout CSV;
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
@@ -191,6 +207,14 @@ COPY testnull FROM stdin WITH NULL AS E'\\0';
SELECT * FROM testnull;
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+42 \\0
+\0 \0
+\.
+
+SELECT * FROM testnull;
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -235,6 +259,23 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+a2
+b
+\.
+SELECT * FROM vistest;
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+d2
+e
+\.
+SELECT * FROM vistest;
+COMMIT;
+SELECT * FROM vistest;
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
x
y
@@ -284,7 +325,7 @@ SELECT * FROM vistest;
COMMIT;
SELECT * FROM vistest;
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -297,6 +338,10 @@ BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
1,,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
+1,,""
+\.
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
@@ -304,6 +349,10 @@ BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
2,'a',,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
+2,'a',,""
+\.
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
-- should fail with not-null constraint violation
@@ -339,6 +388,16 @@ copy check_con_tbl from stdin;
0
\.
select * from check_con_tbl;
+\d+ check_con_tbl
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+1
+\N
+\.
+copy check_con_tbl from stdin with (parallel 1);
+0
+\.
+select * from check_con_tbl;
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
@@ -440,8 +499,149 @@ test1
SELECT * FROM instead_of_insert_tbl;
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+1 11 test_c1 test_d1 test_e1
+2 12 test_c2 test_d2 test_e2
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+3 test_d3
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+4 test_d4
+5 test_d5
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+b d
+\.
+
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+10
+\.
+
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+60,60,60
+\.
+
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+60
+\.
+
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+
+COPY temp_test (a) from stdin with (PARALLEL 1);
+10
+\.
+
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2000 230 23 23
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2001 231 \N \N
+\.
+
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2002 232 40 50 60 70 80
+\.
+
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+x,45,80,90
+x,\x,\\x,\\\x
+x,\,,\\\,,\\
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+3000;;c;;
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+4000:\X:C:\X:\X
+4001:1:empty::
+4002:2:null:\X:\X
+4003:3:Backslash:\\:\\
+4004:4:BackslashX:\\X:\\X
+4005:5:N:\N:\N
+4006:6:BackslashN:\\N:\\N
+4007:7:XX:\XX:\XX
+4008:8:Delimiter:\::\:
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+50003 24 34 44 54
+50004 25 35 45 55
+50005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+60001 22 32 42 52
+60002 23 33 43 53
+60003 24 34 44 54
+60004 25 35 45 55
+60005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
--
1.8.3.1
[text/x-patch] v3-0006-Parallel-Copy-For-Binary-Format-Files.patch (27.3K, ../../CALDaNm3GaZyYPpGu-PpF0SEkJg-eaW3TboHxpxJ-2criv2j_eA@mail.gmail.com/7-v3-0006-Parallel-Copy-For-Binary-Format-Files.patch)
download | inline diff:
From b351a756e59b6b3c21a058394b969877788f43b5 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <[email protected]>
Date: Mon, 3 Aug 2020 11:58:35 +0530
Subject: [PATCH v3 6/6] Parallel Copy For Binary Format Files
Leader reads data from the file into the DSM data blocks each of 64K size.
It also identifies each tuple data block id, start offset, end offset,
tuple size and updates this information in the ring data structure.
Workers parallelly read the tuple information from the ring data structure,
the actual tuple data from the data blocks and parallelly insert the tuples
into the table.
---
src/backend/commands/copy.c | 687 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 602 insertions(+), 85 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 4c69a5d..0948296 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -263,6 +263,17 @@ typedef struct ParallelCopyLineBuf
}ParallelCopyLineBuf;
/*
+ * Represents the usage mode for CopyReadBinaryGetDataBlock.
+ */
+typedef enum FieldInfoType
+{
+ FIELD_NONE = 0,
+ FIELD_COUNT,
+ FIELD_SIZE,
+ FIELD_DATA
+} FieldInfoType;
+
+/*
* Parallel copy data information.
*/
typedef struct ParallelCopyData
@@ -283,6 +294,9 @@ typedef struct ParallelCopyData
/* Current position in worker_line_buf */
uint32 worker_line_buf_pos;
+
+ /* For binary formatted files */
+ ParallelCopyDataBlock *curr_data_block;
}ParallelCopyData;
/*
@@ -447,6 +461,7 @@ typedef struct SerializedParallelCopyState
/* Working state for COPY FROM */
AttrNumber num_defaults;
Oid relid;
+ bool binary;
}SerializedParallelCopyState;
/* DestReceiver for COPY (query) TO */
@@ -521,7 +536,6 @@ typedef struct CopyMultiInsertInfo
int ti_options; /* table insert options */
} CopyMultiInsertInfo;
-
/*
* These macros centralize code used to process line_buf and raw_buf buffers.
* They are macros because they often do continue/break control and to avoid
@@ -649,11 +663,113 @@ if (!IsParallelCopy()) \
else \
return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+/*
+ * CHECK_FIELD_COUNT - Handles the error cases for field count
+ * for binary format files.
+ */
+#define CHECK_FIELD_COUNT \
+{\
+ if (fld_count == -1) \
+ { \
+ if (IsParallelCopy() && \
+ !IsLeader()) \
+ return true; \
+ else if (IsParallelCopy() && \
+ IsLeader()) \
+ { \
+ if (cstate->pcdata->curr_data_block->data[cstate->raw_buf_index + sizeof(fld_count)] != 0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("received copy data after EOF marker"))); \
+ return true; \
+ } \
+ else \
+ { \
+ /* \
+ * Received EOF marker. In a V3-protocol copy, wait for the \
+ * protocol-level EOF, and complain if it doesn't come \
+ * immediately. This ensures that we correctly handle CopyFail, \
+ * if client chooses to send that now. \
+ * \
+ * Note that we MUST NOT try to read more data in an old-protocol \
+ * copy, since there is no protocol-level EOF marker then. We \
+ * could go either way for copy from file, but choose to throw \
+ * error if there's data after the EOF marker, for consistency \
+ * with the new-protocol case. \
+ */ \
+ char dummy; \
+ if (cstate->copy_dest != COPY_OLD_FE && \
+ CopyReadBinaryData(cstate, &dummy, 1) > 0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("received copy data after EOF marker"))); \
+ return false; \
+ } \
+ } \
+ if (fld_count != cstate->max_fields) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("row field count is %d, expected %d", \
+ (int) fld_count, cstate->max_fields))); \
+}
+
+/*
+ * CHECK_FIELD_SIZE - Handles the error case for field size
+ * for binary format files.
+ */
+#define CHECK_FIELD_SIZE(fld_size) \
+{ \
+ if (fld_size < -1) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("invalid field size")));\
+}
+
+/*
+ * EOF_ERROR - Error statement for EOF for binary format
+ * files.
+ */
+#define EOF_ERROR \
+{ \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("unexpected EOF in COPY data")));\
+}
+
+/*
+ * GET_RAW_BUF_INDEX - Calculates the raw buf index for the cases
+ * where the data spread is across multiple data blocks.
+ */
+#define GET_RAW_BUF_INDEX(raw_buf_index, fld_size, required_blks, curr_blk_bytes) \
+{ \
+ raw_buf_index = fld_size - (((required_blks - 1) * DATA_BLOCK_SIZE) + curr_blk_bytes); \
+}
+
+/*
+ * GET_REQUIRED_BLOCKS - Calculates the number of data
+ * blocks required for the cases where the data spread
+ * is across multiple data blocks.
+ */
+#define GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes) \
+{ \
+ /* \
+ * field size can spread across multiple data blocks, \
+ * calculate the number of required data blocks and try to get \
+ * those many data blocks. \
+ */ \
+ required_blks = (int32)(fld_size - curr_blk_bytes)/(int32)DATA_BLOCK_SIZE; \
+ /* \
+ * check if we need the data block for the field data \
+ * bytes that are not modulus of data block size. \
+ */ \
+ if ((fld_size - curr_blk_bytes)%DATA_BLOCK_SIZE != 0) \
+ required_blks++; \
+}
+
/* End parallel copy Macros */
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
-
/* non-export function prototypes */
static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel,
RawStmt *raw_query, Oid queryRelId, List *attnamelist,
@@ -708,6 +824,14 @@ static void ExecBeforeStmtTrigger(CopyState cstate);
static void CheckTargetRelValidity(CopyState cstate);
static void PopulateCstateCatalogInfo(CopyState cstate);
static pg_attribute_always_inline uint32 GetLinePosition(CopyState cstate);
+static uint32 WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info);
+static bool CopyReadBinaryTupleLeader(CopyState cstate);
+static void CopyReadBinaryFindTupleSize(CopyState cstate, uint32 *line_size);
+static bool CopyReadBinaryTupleWorker(CopyState cstate, Datum *values, bool *nulls);
+static Datum CopyReadBinaryAttributeWorker(CopyState cstate, FmgrInfo *flinfo,
+ Oid typioparam, int32 typmod, bool *isnull);
+static void CopyReadBinaryGetDataBlock(CopyState cstate, FieldInfoType field_info);
+
/*
* SerializeParallelCopyState - Copy shared_cstate using cstate information.
*/
@@ -725,6 +849,7 @@ SerializeParallelCopyState(CopyState cstate, SerializedParallelCopyState *shared
shared_cstate->convert_selectively = cstate->convert_selectively;
shared_cstate->num_defaults = cstate->num_defaults;
shared_cstate->relid = cstate->pcdata->relid;
+ shared_cstate->binary = cstate->binary;
}
/*
@@ -884,8 +1009,8 @@ FindInsertMethod(CopyState cstate)
static pg_attribute_always_inline bool
IsParallelCopyAllowed(CopyState cstate)
{
- /* Parallel copy not allowed for frontend (2.0 protocol) & binary option. */
- if ((cstate->copy_dest == COPY_OLD_FE) || cstate->binary)
+ /* Parallel copy not allowed for frontend (2.0 protocol). */
+ if (cstate->copy_dest == COPY_OLD_FE)
return false;
/* Check if copy is into foreign table or temporary table. */
@@ -1155,6 +1280,7 @@ InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
cstate->convert_selectively = shared_cstate->convert_selectively;
cstate->num_defaults = shared_cstate->num_defaults;
pcdata->relid = shared_cstate->relid;
+ cstate->binary = shared_cstate->binary;
PopulateCommonCstateInfo(cstate, tup_desc, attnamelist);
@@ -1543,35 +1669,72 @@ ParallelCopyFrom(CopyState cstate)
ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+ /* raw_buf is not used in parallel copy, instead data blocks are used.*/
+ pfree(cstate->raw_buf);
+
/* Execute the before statement triggers from the leader */
ExecBeforeStmtTrigger(cstate);
- /* On input just throw the header line away. */
- if (cstate->cur_lineno == 0 && cstate->header_line)
+ if (!cstate->binary)
{
- cstate->cur_lineno++;
- if (CopyReadLine(cstate))
+ /* On input just throw the header line away. */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
{
- pcshared_info->is_read_in_progress = false;
- return; /* done */
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ {
+ pcshared_info->is_read_in_progress = false;
+ return; /* done */
+ }
}
- }
- for (;;)
- {
- bool done;
- cstate->cur_lineno++;
+ for (;;)
+ {
+ bool done;
+ cstate->cur_lineno++;
- /* Actually read the line into memory here. */
- done = CopyReadLine(cstate);
+ /* Actually read the line into memory here. */
+ done = CopyReadLine(cstate);
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ break;
+ }
+ }
+ else
+ {
/*
- * EOF at start of line means we're done. If we see EOF after some
- * characters, we act as though it was newline followed by EOF, ie,
- * process the line and then exit loop on next iteration.
+ * Binary Format Files.
+ * For parallel copy leader, fill in the error
+ * context information here, in case any failures
+ * while determining tuple offsets, leader
+ * would throw the errors with proper context.
*/
- if (done && cstate->line_buf.len == 0)
- break;
+ ErrorContextCallback errcallback;
+ errcallback.callback = CopyFromErrorCallback;
+ errcallback.arg = (void *) cstate;
+ errcallback.previous = error_context_stack;
+ error_context_stack = &errcallback;
+ cstate->pcdata->curr_data_block = NULL;
+ cstate->raw_buf_index = 0;
+ pcshared_info->populated = 0;
+ cstate->cur_lineno = 0;
+ cstate->max_fields = list_length(cstate->attnumlist);
+
+ for (;;)
+ {
+ bool eof = false;
+ cstate->cur_lineno++;
+
+ eof = CopyReadBinaryTupleLeader(cstate);
+
+ if (eof)
+ break;
+ }
}
pcshared_info->is_read_in_progress = false;
@@ -1579,7 +1742,355 @@ ParallelCopyFrom(CopyState cstate)
}
/*
- * GetLinePosition - return the line position that worker should process.
+ * CopyReadBinaryGetDataBlock - gets a new block, updates
+ * the current offset, calculates the skip bytes.
+ */
+static void
+CopyReadBinaryGetDataBlock(CopyState cstate, FieldInfoType field_info)
+{
+ ParallelCopyDataBlock *data_block = NULL;
+ ParallelCopyDataBlock *curr_data_block = cstate->pcdata->curr_data_block;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ uint8 move_bytes = 0;
+ uint32 block_pos;
+ uint32 prev_block_pos;
+ int read_bytes = 0;
+
+ prev_block_pos = pcshared_info->cur_block_pos;
+
+ block_pos = WaitGetFreeCopyBlock(pcshared_info);
+
+ if (field_info == FIELD_SIZE || field_info == FIELD_COUNT)
+ move_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+
+ if (curr_data_block != NULL)
+ curr_data_block->skip_bytes = move_bytes;
+
+ data_block = &pcshared_info->data_blocks[block_pos];
+
+ if (move_bytes > 0 && curr_data_block != NULL)
+ memmove(&data_block->data[0], &curr_data_block->data[cstate->raw_buf_index], move_bytes);
+
+ elog(DEBUG1, "LEADER - field info %d is spread across data blocks - moved %d bytes from current block %u to %u block",
+ field_info, move_bytes, prev_block_pos, block_pos);
+
+ read_bytes = CopyGetData(cstate, &data_block->data[move_bytes], 1, (DATA_BLOCK_SIZE - move_bytes));
+
+ if (field_info == FIELD_NONE && cstate->reached_eof)
+ return;
+
+ if (cstate->reached_eof)
+ EOF_ERROR;
+
+ elog(DEBUG1, "LEADER - bytes read from file %d", read_bytes);
+
+ if(field_info == FIELD_SIZE || field_info == FIELD_DATA)
+ {
+ ParallelCopyDataBlock *prev_data_block = NULL;
+ prev_data_block = curr_data_block;
+ prev_data_block->following_block = block_pos;
+
+ if (prev_data_block->curr_blk_completed == false)
+ prev_data_block->curr_blk_completed = true;
+
+ pg_atomic_add_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ }
+
+ cstate->pcdata->curr_data_block = data_block;
+ cstate->raw_buf_index = 0;
+}
+
+/*
+ * CopyReadBinaryTupleLeader - leader reads data from binary formatted file
+ * to data blocks and identifies tuple boundaries/offsets so that workers
+ * can work on the data blocks data.
+ */
+static bool
+CopyReadBinaryTupleLeader(CopyState cstate)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ int16 fld_count;
+ uint32 line_size = 0;
+ uint32 start_block_pos;
+ uint32 start_offset;
+
+ if (cstate->pcdata->curr_data_block == NULL)
+ {
+ CopyReadBinaryGetDataBlock(cstate, FIELD_NONE);
+
+ /*
+ * no data is read from file here. one possibility
+ * to be here could be that the binary file just
+ * has a valid signature but nothing else.
+ */
+ if (cstate->reached_eof)
+ return true;
+ }
+
+ if ((cstate->raw_buf_index + sizeof(fld_count)) >= DATA_BLOCK_SIZE)
+ CopyReadBinaryGetDataBlock(cstate, FIELD_COUNT);
+
+ memcpy(&fld_count, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_count));
+ fld_count = (int16) pg_ntoh16(fld_count);
+ CHECK_FIELD_COUNT;
+ start_offset = cstate->raw_buf_index;
+ cstate->raw_buf_index += sizeof(fld_count);
+ line_size += sizeof(fld_count);
+ start_block_pos = pcshared_info->cur_block_pos;
+
+ CopyReadBinaryFindTupleSize(cstate, &line_size);
+
+ pg_atomic_add_fetch_u32(&cstate->pcdata->curr_data_block->unprocessed_line_parts, 1);
+
+ if (line_size > 0)
+ {
+ int line_pos = UpdateBlockInLineInfo(cstate,
+ start_block_pos,
+ start_offset,
+ line_size,
+ LINE_LEADER_POPULATED);
+
+ pcshared_info->populated++;
+ elog(DEBUG1, "LEADER - adding - block:%u, offset:%u, line size:%u line position:%d",
+ start_block_pos, start_offset, line_size, line_pos);
+ }
+
+ return false;
+}
+
+/*
+ * CopyReadBinaryFindTupleSize - leader identifies boundaries/
+ * offsets for each attribute/column and finally results in the
+ * tuple/row size. It moves on to next data block if the attribute/
+ * column is spread across data blocks.
+ */
+static void
+CopyReadBinaryFindTupleSize(CopyState cstate, uint32 *line_size)
+{
+ int32 fld_size;
+ ListCell *cur;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ foreach(cur, cstate->attnumlist)
+ {
+ int att_num = lfirst_int(cur);
+ Form_pg_attribute att = TupleDescAttr(tup_desc, (att_num - 1));
+ cstate->cur_attname = NameStr(att->attname);
+ fld_size = 0;
+
+ if ((cstate->raw_buf_index + sizeof(fld_size)) >= DATA_BLOCK_SIZE)
+ CopyReadBinaryGetDataBlock(cstate, FIELD_SIZE);
+
+ memcpy(&fld_size, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_size));
+ cstate->raw_buf_index += sizeof(fld_size);
+ *line_size += sizeof(fld_size);
+ fld_size = (int32) pg_ntoh32(fld_size);
+
+ CHECK_FIELD_SIZE(fld_size);
+
+ *line_size += fld_size;
+
+ if ((DATA_BLOCK_SIZE - cstate->raw_buf_index) >= fld_size)
+ {
+ cstate->raw_buf_index += fld_size;
+ elog(DEBUG1, "LEADER - tuple lies in he same data block");
+ }
+ else
+ {
+ int32 required_blks = 0;
+ int32 curr_blk_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+ int i = 0;
+
+ GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes);
+
+ i = required_blks;
+
+ while(i > 0)
+ {
+ CopyReadBinaryGetDataBlock(cstate, FIELD_DATA);
+ i--;
+ }
+
+ GET_RAW_BUF_INDEX(cstate->raw_buf_index, fld_size, required_blks, curr_blk_bytes);
+
+ /*
+ * raw_buf_index should never cross data block size,
+ * as the required number of data blocks would have
+ * been obtained in the above while loop.
+ */
+ Assert(cstate->raw_buf_index <= DATA_BLOCK_SIZE);
+ }
+ cstate->cur_attname = NULL;
+ }
+}
+
+/*
+ * CopyReadBinaryTupleWorker - each worker reads data from data blocks after
+ * getting leader-identified tuple offsets from ring data structure.
+ */
+static bool
+CopyReadBinaryTupleWorker(CopyState cstate, Datum *values, bool *nulls)
+{
+ uint32 line_pos;
+ ParallelCopyLineBoundary *line_info;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ int16 fld_count;
+ ListCell *cur;
+ FmgrInfo *in_functions = cstate->in_functions;
+ Oid *typioparams = cstate->typioparams;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ line_pos = GetLinePosition(cstate);
+
+ if (line_pos == -1)
+ return true;
+
+ line_info = &pcshared_info->line_boundaries.ring[line_pos];
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[line_info->first_block];
+ cstate->raw_buf_index = line_info->start_offset;
+
+ if (cstate->raw_buf_index + sizeof(fld_count) >= DATA_BLOCK_SIZE)
+ {
+ /*
+ * The case where field count spread across datablocks should never occur,
+ * as the leader would have moved it to next block. this code exists for
+ * debugging purposes only.
+ */
+ elog(DEBUG1, "WORKER - field count spread across datablocks should never occur");
+ }
+
+ memcpy(&fld_count, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_count));
+ fld_count = (int16) pg_ntoh16(fld_count);
+
+ CHECK_FIELD_COUNT;
+
+ cstate->raw_buf_index += sizeof(fld_count);
+
+ foreach(cur, cstate->attnumlist)
+ {
+ int att_num = lfirst_int(cur);
+ int m = att_num - 1;
+ Form_pg_attribute att = TupleDescAttr(tup_desc, m);
+ cstate->cur_attname = NameStr(att->attname);
+
+ values[m] = CopyReadBinaryAttributeWorker(cstate,
+ &in_functions[m],
+ typioparams[m],
+ att->atttypmod,
+ &nulls[m]);
+ cstate->cur_attname = NULL;
+ }
+
+ pg_atomic_sub_fetch_u32(&cstate->pcdata->curr_data_block->unprocessed_line_parts, 1);
+ line_info->start_offset = -1;
+ pg_atomic_write_u32(&line_info->line_state, LINE_WORKER_PROCESSED);
+ pg_atomic_write_u32(&line_info->line_size, -1);
+ pg_atomic_add_fetch_u64(&pcshared_info->total_worker_processed, 1);
+
+ return false;
+}
+
+/*
+ * CopyReadBinaryAttributeWorker - leader identifies boundaries/offsets
+ * for each attribute/column, it moves on to next data block if the
+ * attribute/column is spread across data blocks.
+ */
+static Datum
+CopyReadBinaryAttributeWorker(CopyState cstate, FmgrInfo *flinfo,
+ Oid typioparam, int32 typmod, bool *isnull)
+{
+ int32 fld_size;
+ Datum result;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+
+ if ((cstate->raw_buf_index + sizeof(fld_size)) >= DATA_BLOCK_SIZE)
+ {
+ ParallelCopyDataBlock *prev_data_block = cstate->pcdata->curr_data_block;
+ elog(DEBUG1, "WORKER - field size is spread across data blocks");
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[prev_data_block->following_block];
+ pg_atomic_sub_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ cstate->raw_buf_index = 0;
+ }
+
+ memcpy(&fld_size, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_size));
+ fld_size = (int32) pg_ntoh32(fld_size);
+
+ CHECK_FIELD_SIZE(fld_size);
+
+ cstate->raw_buf_index += sizeof(fld_size);
+
+ /* reset attribute_buf to empty, and load raw data in it */
+ resetStringInfo(&cstate->attribute_buf);
+
+ enlargeStringInfo(&cstate->attribute_buf, fld_size);
+
+ if ((DATA_BLOCK_SIZE - cstate->raw_buf_index) >= fld_size)
+ {
+ elog(DEBUG1, "WORKER - tuple lies in single data block");
+ memcpy(&cstate->attribute_buf.data[0],&cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], fld_size);
+ cstate->raw_buf_index += fld_size;
+ }
+ else
+ {
+ uint32 att_buf_idx = 0;
+ uint32 copy_bytes = 0;
+ int32 required_blks = 0;
+ int32 curr_blk_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+ ParallelCopyDataBlock *prev_data_block = NULL;
+ int i = 0;
+
+ GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes);
+
+ i = required_blks;
+ prev_data_block = cstate->pcdata->curr_data_block;
+ elog(DEBUG1, "WORKER - tuple is spread across data blocks");
+ memcpy(&cstate->attribute_buf.data[0], &prev_data_block->data[cstate->raw_buf_index],
+ curr_blk_bytes);
+ copy_bytes = curr_blk_bytes;
+ att_buf_idx = curr_blk_bytes;
+
+ while (i>0)
+ {
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[prev_data_block->following_block];
+ pg_atomic_sub_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ cstate->raw_buf_index = 0;
+ copy_bytes = fld_size - att_buf_idx;
+
+ /*
+ * the bytes that are yet to be taken into att buff are more than
+ * the entire data block size, but only take the data block size
+ * elements.
+ */
+ if (copy_bytes >= DATA_BLOCK_SIZE)
+ copy_bytes = DATA_BLOCK_SIZE;
+
+ memcpy(&cstate->attribute_buf.data[att_buf_idx],
+ &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], copy_bytes);
+ att_buf_idx += copy_bytes;
+ i--;
+ }
+ GET_RAW_BUF_INDEX(cstate->raw_buf_index, fld_size, required_blks, curr_blk_bytes);
+ }
+
+ cstate->attribute_buf.len = fld_size;
+ cstate->attribute_buf.data[fld_size] = '\0';
+
+ /* Call the column type's binary input converter */
+ result = ReceiveFunctionCall(flinfo, &cstate->attribute_buf,
+ typioparam, typmod);
+
+ /* Trouble if it didn't eat the whole buffer */
+ if (cstate->attribute_buf.cursor != cstate->attribute_buf.len)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("incorrect binary data format")));
+
+ *isnull = false;
+ return result;
+}
+
+/*
+ * GetLinePosition - return the line position that worker should pcdata->process.
*/
static uint32
GetLinePosition(CopyState cstate)
@@ -1667,7 +2178,9 @@ GetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
{
dataBlkPtr->curr_blk_completed = false;
dataBlkPtr->skip_bytes = 0;
+ dataBlkPtr->following_block = -1;
pcshared_info->cur_block_pos = block_pos;
+ MemSet(&dataBlkPtr->data[0], 0, DATA_BLOCK_SIZE);
return block_pos;
}
@@ -2183,10 +2696,26 @@ CopyGetInt32(CopyState cstate, int32 *val)
{
uint32 buf;
- if (CopyReadBinaryData(cstate, (char *) &buf, sizeof(buf)) != sizeof(buf))
+ /*
+ * For parallel copy, avoid reading data to raw buf, read directly
+ * from file, later the data will be read to parallel copy data
+ * buffers.
+ */
+ if (cstate->nworkers > 0)
{
- *val = 0; /* suppress compiler warning */
- return false;
+ if (CopyGetData(cstate, &buf, sizeof(buf), sizeof(buf)) != sizeof(buf))
+ {
+ *val = 0; /* suppress compiler warning */
+ return false;
+ }
+ }
+ else
+ {
+ if (CopyReadBinaryData(cstate, (char *) &buf, sizeof(buf)) != sizeof(buf))
+ {
+ *val = 0; /* suppress compiler warning */
+ return false;
+ }
}
*val = (int32) pg_ntoh32(buf);
return true;
@@ -2575,7 +3104,15 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
EndParallelCopy(pcxt);
}
else
+ {
+ /*
+ * Reset nworkers to -1 here. This is useful in cases where user
+ * specifies parallel workers, but, no worker is picked up, so go
+ * back to non parallel mode value of nworkers.
+ */
+ cstate->nworkers = -1;
*processed = CopyFrom(cstate); /* copy from file to database */
+ }
EndCopyFrom(cstate);
}
@@ -5040,7 +5577,7 @@ BeginCopyFrom(ParseState *pstate,
* only in text mode.
*/
initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (IsParallelCopy()) ? NULL : (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
cstate->raw_buf_index = cstate->raw_buf_len = 0;
if (!cstate->binary)
{
@@ -5120,7 +5657,7 @@ BeginCopyFrom(ParseState *pstate,
int32 tmp;
/* Signature */
- if (CopyReadBinaryData(cstate, readSig, 11) != 11 ||
+ if (CopyGetData(cstate, readSig, 11, 11) != 11 ||
memcmp(readSig, BinarySignature, 11) != 0)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
@@ -5148,7 +5685,7 @@ BeginCopyFrom(ParseState *pstate,
/* Skip extension header, if present */
while (tmp-- > 0)
{
- if (CopyReadBinaryData(cstate, readSig, 1) != 1)
+ if (CopyGetData(cstate, readSig, 1, 1) != 1)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("invalid COPY file header (wrong length)")));
@@ -5345,60 +5882,45 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext,
else
{
/* binary */
- int16 fld_count;
- ListCell *cur;
-
cstate->cur_lineno++;
+ cstate->max_fields = list_length(cstate->attnumlist);
- if (!CopyGetInt16(cstate, &fld_count))
+ if (!IsParallelCopy())
{
- /* EOF detected (end of file, or protocol-level EOF) */
- return false;
- }
+ int16 fld_count;
+ ListCell *cur;
- if (fld_count == -1)
- {
- /*
- * Received EOF marker. In a V3-protocol copy, wait for the
- * protocol-level EOF, and complain if it doesn't come
- * immediately. This ensures that we correctly handle CopyFail,
- * if client chooses to send that now.
- *
- * Note that we MUST NOT try to read more data in an old-protocol
- * copy, since there is no protocol-level EOF marker then. We
- * could go either way for copy from file, but choose to throw
- * error if there's data after the EOF marker, for consistency
- * with the new-protocol case.
- */
- char dummy;
-
- if (cstate->copy_dest != COPY_OLD_FE &&
- CopyReadBinaryData(cstate, &dummy, 1) > 0)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("received copy data after EOF marker")));
- return false;
- }
+ if (!CopyGetInt16(cstate, &fld_count))
+ {
+ /* EOF detected (end of file, or protocol-level EOF) */
+ return false;
+ }
- if (fld_count != attr_count)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("row field count is %d, expected %d",
- (int) fld_count, attr_count)));
+ CHECK_FIELD_COUNT;
- foreach(cur, cstate->attnumlist)
+ foreach(cur, cstate->attnumlist)
+ {
+ int attnum = lfirst_int(cur);
+ int m = attnum - 1;
+ Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+
+ cstate->cur_attname = NameStr(att->attname);
+ values[m] = CopyReadBinaryAttribute(cstate,
+ &in_functions[m],
+ typioparams[m],
+ att->atttypmod,
+ &nulls[m]);
+ cstate->cur_attname = NULL;
+ }
+ }
+ else
{
- int attnum = lfirst_int(cur);
- int m = attnum - 1;
- Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+ bool eof = false;
- cstate->cur_attname = NameStr(att->attname);
- values[m] = CopyReadBinaryAttribute(cstate,
- &in_functions[m],
- typioparams[m],
- att->atttypmod,
- &nulls[m]);
- cstate->cur_attname = NULL;
+ eof = CopyReadBinaryTupleWorker(cstate, values, nulls);
+
+ if (eof)
+ return false;
}
}
@@ -6398,18 +6920,15 @@ CopyReadBinaryAttribute(CopyState cstate, FmgrInfo *flinfo,
Datum result;
if (!CopyGetInt32(cstate, &fld_size))
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("unexpected EOF in COPY data")));
+ EOF_ERROR;
+
if (fld_size == -1)
{
*isnull = true;
return ReceiveFunctionCall(flinfo, NULL, typioparam, typmod);
}
- if (fld_size < 0)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("invalid field size")));
+
+ CHECK_FIELD_SIZE(fld_size);
/* reset attribute_buf to empty, and load raw data in it */
resetStringInfo(&cstate->attribute_buf);
@@ -6417,9 +6936,7 @@ CopyReadBinaryAttribute(CopyState cstate, FmgrInfo *flinfo,
enlargeStringInfo(&cstate->attribute_buf, fld_size);
if (CopyReadBinaryData(cstate, cstate->attribute_buf.data,
fld_size) != fld_size)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("unexpected EOF in COPY data")));
+ EOF_ERROR;
cstate->attribute_buf.len = fld_size;
cstate->attribute_buf.data[fld_size] = '\0';
--
1.8.3.1
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-17 04:14 Greg Nancarrow <[email protected]>
parent: vignesh C <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Greg Nancarrow @ 2020-08-17 04:14 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
Hi Vignesh,
Some further comments:
(1) v3-0002-Framework-for-leader-worker-in-parallel-copy.patch
+/*
+ * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
+ * block to process to avoid lock contention. This value should be divisible by
+ * RINGSIZE, as wrap around cases is currently not handled while selecting the
+ * WORKER_CHUNK_COUNT by the worker.
+ */
+#define WORKER_CHUNK_COUNT 50
"This value should be divisible by RINGSIZE" is not a correct
statement (since obviously 50 is not divisible by 10000).
It should say something like "This value should evenly divide into
RINGSIZE", or "RINGSIZE should be a multiple of WORKER_CHUNK_COUNT".
(2) v3-0003-Allow-copy-from-command-to-process-data-from-file.patch
(i)
+ /*
+ * If the data is present in current block
lineInfo. line_size
+ * will be updated. If the data is spread
across the blocks either
Somehow a space has been put between "lineinfo." and "line_size".
It should be: "If the data is present in current block
lineInfo.line_size will be updated"
(ii)
>This is not possible because of pg_atomic_compare_exchange_u32, this
>will succeed only for one of the workers whose line_state is
>LINE_LEADER_POPULATED, for other workers it will fail. This is
>explained in detail above ParallelCopyLineBoundary.
Yes, but prior to that call to pg_atomic_compare_exchange_u32(),
aren't you separately reading line_state and line_state, so that
between those reads, it may have transitioned from leader to another
worker, such that the read line state ("cur_line_state", being checked
in the if block) may not actually match what is now in the line_state
and/or the read line_size ("dataSize") doesn't actually correspond to
the read line state?
(sorry, still not 100% convinced that the synchronization and checks
are safe in all cases)
(3) v3-0006-Parallel-Copy-For-Binary-Format-Files.patch
>raw_buf is not used in parallel copy, instead raw_buf will be pointing
>to shared memory data blocks. This memory was allocated as part of
>BeginCopyFrom, uptil this point we cannot be 100% sure as copy can be
>performed sequentially like in case max_worker_processes is not
>available, if it switches to sequential mode raw_buf will be used
>while performing copy operation. At this place we can safely free this
>memory that was allocated
So the following code (which checks raw_buf, which still points to
memory that has been pfreed) is still valid?
In the SetRawBufForLoad() function, which is called by CopyReadLineText():
cur_data_blk_ptr = (cstate->raw_buf) ?
&pcshared_info->data_blocks[cur_block_pos] : NULL;
The above code looks a bit dicey to me. I stepped over that line in
the debugger when I debugged an instance of Parallel Copy, so it
definitely gets executed.
It makes me wonder what other code could possibly be checking raw_buf
and using it in some way, when in fact what it points to has been
pfreed.
Are you able to add the following line of code, or will it (somehow)
break logic that you are relying on?
pfree(cstate->raw_buf);
cstate->raw_buf = NULL; <=== I suggest that this line is added
Regards,
Greg Nancarrow
Fujitsu Australia
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-19 06:21 vignesh C <[email protected]>
parent: Greg Nancarrow <[email protected]>
0 siblings, 2 replies; 2431+ messages in thread
From: vignesh C @ 2020-08-19 06:21 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
Thanks Greg for reviewing the patch. Please find my thoughts for your comments.
On Mon, Aug 17, 2020 at 9:44 AM Greg Nancarrow <[email protected]> wrote:
> Some further comments:
>
> (1) v3-0002-Framework-for-leader-worker-in-parallel-copy.patch
>
> +/*
> + * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
> + * block to process to avoid lock contention. This value should be divisible by
> + * RINGSIZE, as wrap around cases is currently not handled while selecting the
> + * WORKER_CHUNK_COUNT by the worker.
> + */
> +#define WORKER_CHUNK_COUNT 50
>
>
> "This value should be divisible by RINGSIZE" is not a correct
> statement (since obviously 50 is not divisible by 10000).
> It should say something like "This value should evenly divide into
> RINGSIZE", or "RINGSIZE should be a multiple of WORKER_CHUNK_COUNT".
>
Fixed. Changed it to RINGSIZE should be a multiple of WORKER_CHUNK_COUNT.
> (2) v3-0003-Allow-copy-from-command-to-process-data-from-file.patch
>
> (i)
>
> + /*
> + * If the data is present in current block
> lineInfo. line_size
> + * will be updated. If the data is spread
> across the blocks either
>
> Somehow a space has been put between "lineinfo." and "line_size".
> It should be: "If the data is present in current block
> lineInfo.line_size will be updated"
Fixed, changed it to lineinfo->line_size.
>
> (ii)
>
> >This is not possible because of pg_atomic_compare_exchange_u32, this
> >will succeed only for one of the workers whose line_state is
> >LINE_LEADER_POPULATED, for other workers it will fail. This is
> >explained in detail above ParallelCopyLineBoundary.
>
> Yes, but prior to that call to pg_atomic_compare_exchange_u32(),
> aren't you separately reading line_state and line_state, so that
> between those reads, it may have transitioned from leader to another
> worker, such that the read line state ("cur_line_state", being checked
> in the if block) may not actually match what is now in the line_state
> and/or the read line_size ("dataSize") doesn't actually correspond to
> the read line state?
>
> (sorry, still not 100% convinced that the synchronization and checks
> are safe in all cases)
>
I think that you are describing about the problem could happen in the
following case:
when we read curr_line_state, the value was LINE_WORKER_PROCESSED or
LINE_WORKER_PROCESSING. Then in some cases if the leader is very fast
compared to the workers then the leader quickly populates one line and
sets the state to LINE_LEADER_POPULATED. State is changed to
LINE_LEADER_POPULATED when we are checking the currr_line_state.
I feel this will not be a problem because, Leader will populate & wait
till some RING element is available to populate. In the meantime
worker has seen that state is LINE_WORKER_PROCESSED or
LINE_WORKER_PROCESSING(previous state that it read), worker has
identified that this chunk was processed by some other worker, worker
will move and try to get the next available chunk & insert those
records. It will keep continuing till it gets the next chunk to
process. Eventually one of the workers will get this chunk and process
it.
> (3) v3-0006-Parallel-Copy-For-Binary-Format-Files.patch
>
> >raw_buf is not used in parallel copy, instead raw_buf will be pointing
> >to shared memory data blocks. This memory was allocated as part of
> >BeginCopyFrom, uptil this point we cannot be 100% sure as copy can be
> >performed sequentially like in case max_worker_processes is not
> >available, if it switches to sequential mode raw_buf will be used
> >while performing copy operation. At this place we can safely free this
> >memory that was allocated
>
> So the following code (which checks raw_buf, which still points to
> memory that has been pfreed) is still valid?
>
> In the SetRawBufForLoad() function, which is called by CopyReadLineText():
>
> cur_data_blk_ptr = (cstate->raw_buf) ?
> &pcshared_info->data_blocks[cur_block_pos] : NULL;
>
> The above code looks a bit dicey to me. I stepped over that line in
> the debugger when I debugged an instance of Parallel Copy, so it
> definitely gets executed.
> It makes me wonder what other code could possibly be checking raw_buf
> and using it in some way, when in fact what it points to has been
> pfreed.
>
> Are you able to add the following line of code, or will it (somehow)
> break logic that you are relying on?
>
> pfree(cstate->raw_buf);
> cstate->raw_buf = NULL; <=== I suggest that this line is added
>
You are right, I have debugged & verified it sets it to an invalid
block which is not expected. There are chances this would have caused
some corruption in some machines. The suggested fix is required, I
have fixed it. I have moved this change to
0003-Allow-copy-from-command-to-process-data-from-file.patch as
0006-Parallel-Copy-For-Binary-Format-Files is only for Binary format
parallel copy & that change is common change for parallel copy.
I have attached new set of patches with the fixes.
Thoughts?
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
Attachments:
[text/x-patch] v4-0001-Copy-code-readjustment-to-support-parallel-copy.patch (16.7K, ../../CALDaNm2QD5yAsMsgZ-Lr1rqGeCxLcQu7CVrS=Jy3AnGWKDS6NA@mail.gmail.com/2-v4-0001-Copy-code-readjustment-to-support-parallel-copy.patch)
download | inline diff:
From 896cb1f96f0a2215a097fffce94b50b2872b2c81 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 08:52:48 +0530
Subject: [PATCH v4 1/6] Copy code readjustment to support parallel copy.
This patch has the copy code slightly readjusted so that the common code is
separated to functions/macros, these functions/macros will be used by the
workers in the parallel copy code of the upcoming patches. EOL removal is moved
from CopyReadLine to CopyReadLineText, this change was required because in case
of parallel copy the record identification and record updation is done in
CopyReadLineText, before record information is updated in shared memory the new
line characters should be removed.
---
src/backend/commands/copy.c | 361 ++++++++++++++++++++++++++------------------
1 file changed, 218 insertions(+), 143 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index db7d24a..436e458 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -95,6 +95,9 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
+
/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
@@ -224,7 +227,6 @@ typedef struct CopyStateData
* appropriate amounts of data from this buffer. In both modes, we
* guarantee that there is a \0 at raw_buf[raw_buf_len].
*/
-#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
@@ -354,6 +356,27 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/*
+ * CLEAR_EOL_LINE - Wrapper for clearing EOL.
+ */
+#define CLEAR_EOL_LINE() \
+if (!result && !IsHeaderLine()) \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len) \
+
+/*
+ * INCREMENTPROCESSED - Increment the lines processed.
+ */
+#define INCREMENTPROCESSED(processed) \
+processed++;
+
+/*
+ * RETURNPROCESSED - Get the lines processed.
+ */
+#define RETURNPROCESSED(processed) \
+return processed;
+
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -401,7 +424,11 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
-
+static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
+ List *attnamelist);
+static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size);
+static void ConvertToServerEncoding(CopyState cstate);
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -801,14 +828,18 @@ CopyLoadRawBuf(CopyState cstate)
{
int nbytes = RAW_BUF_BYTES(cstate);
int inbytes;
+ int minread = 1;
/* Copy down the unprocessed data if any. */
if (nbytes > 0)
memmove(cstate->raw_buf, cstate->raw_buf + cstate->raw_buf_index,
nbytes);
+ if (cstate->copy_dest == COPY_NEW_FE)
+ minread = RAW_BUF_SIZE - nbytes;
+
inbytes = CopyGetData(cstate, cstate->raw_buf + nbytes,
- 1, RAW_BUF_SIZE - nbytes);
+ minread, RAW_BUF_SIZE - nbytes);
nbytes += inbytes;
cstate->raw_buf[nbytes] = '\0';
cstate->raw_buf_index = 0;
@@ -1514,7 +1545,6 @@ BeginCopy(ParseState *pstate,
{
CopyState cstate;
TupleDesc tupDesc;
- int num_phys_attrs;
MemoryContext oldcontext;
/* Allocate workspace and zero all fields */
@@ -1680,6 +1710,24 @@ BeginCopy(ParseState *pstate,
tupDesc = cstate->queryDesc->tupDesc;
}
+ PopulateCommonCstateInfo(cstate, tupDesc, attnamelist);
+ cstate->copy_dest = COPY_FILE; /* default */
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return cstate;
+}
+
+/*
+ * PopulateCommonCstateInfo - Populates the common variables required for copy
+ * from operation. This is a helper function for BeginCopy function.
+ */
+static void
+PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc,
+ List *attnamelist)
+{
+ int num_phys_attrs;
+
/* Generate or convert list of attributes to process */
cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
@@ -1799,12 +1847,6 @@ BeginCopy(ParseState *pstate,
pg_database_encoding_max_length() > 1);
/* See Multibyte encoding comment above */
cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->file_encoding);
-
- cstate->copy_dest = COPY_FILE; /* default */
-
- MemoryContextSwitchTo(oldcontext);
-
- return cstate;
}
/*
@@ -2696,32 +2738,11 @@ CopyMultiInsertInfoStore(CopyMultiInsertInfo *miinfo, ResultRelInfo *rri,
}
/*
- * Copy FROM file to relation.
+ * Check if the relation specified in copy from is valid.
*/
-uint64
-CopyFrom(CopyState cstate)
+static void
+CheckTargetRelValidity(CopyState cstate)
{
- ResultRelInfo *resultRelInfo;
- ResultRelInfo *target_resultRelInfo;
- ResultRelInfo *prevResultRelInfo = NULL;
- EState *estate = CreateExecutorState(); /* for ExecConstraints() */
- ModifyTableState *mtstate;
- ExprContext *econtext;
- TupleTableSlot *singleslot = NULL;
- MemoryContext oldcontext = CurrentMemoryContext;
-
- PartitionTupleRouting *proute = NULL;
- ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
- int ti_options = 0; /* start with default options for insert */
- BulkInsertState bistate = NULL;
- CopyInsertMethod insertMethod;
- CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
- uint64 processed = 0;
- bool has_before_insert_row_trig;
- bool has_instead_insert_row_trig;
- bool leafpart_use_multi_insert = false;
-
Assert(cstate->rel);
/*
@@ -2758,27 +2779,6 @@ CopyFrom(CopyState cstate)
RelationGetRelationName(cstate->rel))));
}
- /*
- * If the target file is new-in-transaction, we assume that checking FSM
- * for free space is a waste of time. This could possibly be wrong, but
- * it's unlikely.
- */
- if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
- (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
- cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
- ti_options |= TABLE_INSERT_SKIP_FSM;
-
- /*
- * Optimize if new relfilenode was created in this subxact or one of its
- * committed children and we won't see those rows later as part of an
- * earlier scan or command. The subxact test ensures that if this subxact
- * aborts then the frozen rows won't be visible after xact cleanup. Note
- * that the stronger test of exactly which subtransaction created it is
- * crucial for correctness of this optimization. The test for an earlier
- * scan or command tolerates false negatives. FREEZE causes other sessions
- * to see rows they would not see under MVCC, and a false negative merely
- * spreads that anomaly to the current session.
- */
if (cstate->freeze)
{
/*
@@ -2816,9 +2816,61 @@ CopyFrom(CopyState cstate)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot perform COPY FREEZE because the table was not created or truncated in the current subtransaction")));
+ }
+}
+
+/*
+ * Copy FROM file to relation.
+ */
+uint64
+CopyFrom(CopyState cstate)
+{
+ ResultRelInfo *resultRelInfo;
+ ResultRelInfo *target_resultRelInfo;
+ ResultRelInfo *prevResultRelInfo = NULL;
+ EState *estate = CreateExecutorState(); /* for ExecConstraints() */
+ ModifyTableState *mtstate;
+ ExprContext *econtext;
+ TupleTableSlot *singleslot = NULL;
+ MemoryContext oldcontext = CurrentMemoryContext;
+
+ PartitionTupleRouting *proute = NULL;
+ ErrorContextCallback errcallback;
+ CommandId mycid = GetCurrentCommandId(true);
+ int ti_options = 0; /* start with default options for insert */
+ BulkInsertState bistate = NULL;
+ CopyInsertMethod insertMethod;
+ CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
+ uint64 processed = 0;
+ bool has_before_insert_row_trig;
+ bool has_instead_insert_row_trig;
+ bool leafpart_use_multi_insert = false;
+
+ CheckTargetRelValidity(cstate);
+ /*
+ * If the target file is new-in-transaction, we assume that checking FSM
+ * for free space is a waste of time. This could possibly be wrong, but
+ * it's unlikely.
+ */
+ if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
+ (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
+ cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
+ ti_options |= TABLE_INSERT_SKIP_FSM;
+
+ /*
+ * Optimize if new relfilenode was created in this subxact or one of its
+ * committed children and we won't see those rows later as part of an
+ * earlier scan or command. The subxact test ensures that if this subxact
+ * aborts then the frozen rows won't be visible after xact cleanup. Note
+ * that the stronger test of exactly which subtransaction created it is
+ * crucial for correctness of this optimization. The test for an earlier
+ * scan or command tolerates false negatives. FREEZE causes other sessions
+ * to see rows they would not see under MVCC, and a false negative merely
+ * spreads that anomaly to the current session.
+ */
+ if (cstate->freeze)
ti_options |= TABLE_INSERT_FROZEN;
- }
/*
* We need a ResultRelInfo so we can use the regular executor's
@@ -3311,7 +3363,7 @@ CopyFrom(CopyState cstate)
* or FDW; this is the same definition used by nodeModifyTable.c
* for counting tuples inserted by an INSERT command.
*/
- processed++;
+ INCREMENTPROCESSED(processed)
}
}
@@ -3366,30 +3418,15 @@ CopyFrom(CopyState cstate)
FreeExecutorState(estate);
- return processed;
+ RETURNPROCESSED(processed)
}
/*
- * Setup to read tuples from a file for COPY FROM.
- *
- * 'rel': Used as a template for the tuples
- * 'filename': Name of server-local file to read
- * 'attnamelist': List of char *, columns to include. NIL selects all cols.
- * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
- *
- * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ * PopulateCstateCatalogInfo - populate the catalog information.
*/
-CopyState
-BeginCopyFrom(ParseState *pstate,
- Relation rel,
- const char *filename,
- bool is_program,
- copy_data_source_cb data_source_cb,
- List *attnamelist,
- List *options)
+static void
+PopulateCstateCatalogInfo(CopyState cstate)
{
- CopyState cstate;
- bool pipe = (filename == NULL);
TupleDesc tupDesc;
AttrNumber num_phys_attrs,
num_defaults;
@@ -3399,38 +3436,8 @@ BeginCopyFrom(ParseState *pstate,
Oid in_func_oid;
int *defmap;
ExprState **defexprs;
- MemoryContext oldcontext;
bool volatile_defexprs;
- cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
- oldcontext = MemoryContextSwitchTo(cstate->copycontext);
-
- /* Initialize state variables */
- cstate->reached_eof = false;
- cstate->eol_type = EOL_UNKNOWN;
- cstate->cur_relname = RelationGetRelationName(cstate->rel);
- cstate->cur_lineno = 0;
- cstate->cur_attname = NULL;
- cstate->cur_attval = NULL;
-
- /*
- * Set up variables to avoid per-attribute overhead. attribute_buf and
- * raw_buf are used in both text and binary modes, but we use line_buf
- * only in text mode.
- */
- initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
- cstate->raw_buf_index = cstate->raw_buf_len = 0;
- if (!cstate->binary)
- {
- initStringInfo(&cstate->line_buf);
- cstate->line_buf_converted = false;
- }
-
- /* Assign range table, we'll need it in CopyFrom. */
- if (pstate)
- cstate->range_table = pstate->p_rtable;
-
tupDesc = RelationGetDescr(cstate->rel);
num_phys_attrs = tupDesc->natts;
num_defaults = 0;
@@ -3508,6 +3515,61 @@ BeginCopyFrom(ParseState *pstate,
cstate->defexprs = defexprs;
cstate->volatile_defexprs = volatile_defexprs;
cstate->num_defaults = num_defaults;
+}
+
+/*
+ * Setup to read tuples from a file for COPY FROM.
+ *
+ * 'rel': Used as a template for the tuples
+ * 'filename': Name of server-local file to read
+ * 'attnamelist': List of char *, columns to include. NIL selects all cols.
+ * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
+ *
+ * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ */
+CopyState
+BeginCopyFrom(ParseState *pstate,
+ Relation rel,
+ const char *filename,
+ bool is_program,
+ copy_data_source_cb data_source_cb,
+ List *attnamelist,
+ List *options)
+{
+ CopyState cstate;
+ bool pipe = (filename == NULL);
+ MemoryContext oldcontext;
+
+ cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ /* Initialize state variables */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /*
+ * Set up variables to avoid per-attribute overhead. attribute_buf is
+ * used in both text and binary modes, but we use line_buf and raw_buf
+ * only in text mode.
+ */
+ initStringInfo(&cstate->attribute_buf);
+ cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ if (!cstate->binary)
+ {
+ initStringInfo(&cstate->line_buf);
+ cstate->line_buf_converted = false;
+ }
+
+ /* Assign range table, we'll need it in CopyFrom. */
+ if (pstate)
+ cstate->range_table = pstate->p_rtable;
+
+ PopulateCstateCatalogInfo(cstate);
cstate->is_program = is_program;
if (data_source_cb)
@@ -3917,45 +3979,60 @@ CopyReadLine(CopyState cstate)
} while (CopyLoadRawBuf(cstate));
}
}
- else
- {
- /*
- * If we didn't hit EOF, then we must have transferred the EOL marker
- * to line_buf along with the data. Get rid of it.
- */
- switch (cstate->eol_type)
- {
- case EOL_NL:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CR:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\r');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CRNL:
- Assert(cstate->line_buf.len >= 2);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 2] == '\r');
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len -= 2;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_UNKNOWN:
- /* shouldn't get here */
- Assert(false);
- break;
- }
- }
+ ConvertToServerEncoding(cstate);
+ return result;
+}
+
+/*
+ * ClearEOLFromCopiedData - Clear EOL from the copied data.
+ */
+static void
+ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size)
+{
+ /*
+ * If we didn't hit EOF, then we must have transferred the EOL marker
+ * to line_buf along with the data. Get rid of it.
+ */
+ switch (cstate->eol_type)
+ {
+ case EOL_NL:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CR:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\r');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CRNL:
+ Assert(*copy_line_size >= 2);
+ Assert(copy_line_data[copy_line_pos - 2] == '\r');
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 2] = '\0';
+ *copy_line_size -= 2;
+ break;
+ case EOL_UNKNOWN:
+ /* shouldn't get here */
+ Assert(false);
+ break;
+ }
+}
+
+/*
+ * ConvertToServerEncoding - convert contents to server encoding.
+ */
+static void
+ConvertToServerEncoding(CopyState cstate)
+{
/* Done reading the line. Convert it to server encoding. */
if (cstate->need_transcoding)
{
char *cvt;
-
cvt = pg_any_to_server(cstate->line_buf.data,
cstate->line_buf.len,
cstate->file_encoding);
@@ -3967,11 +4044,8 @@ CopyReadLine(CopyState cstate)
pfree(cvt);
}
}
-
/* Now it's safe to use the buffer in error messages */
cstate->line_buf_converted = true;
-
- return result;
}
/*
@@ -4334,6 +4408,7 @@ not_end_of_copy:
* Transfer any still-uncopied data to line_buf.
*/
REFILL_LINEBUF;
+ CLEAR_EOL_LINE();
return result;
}
--
1.8.3.1
[text/x-patch] v4-0002-Framework-for-leader-worker-in-parallel-copy.patch (31.1K, ../../CALDaNm2QD5yAsMsgZ-Lr1rqGeCxLcQu7CVrS=Jy3AnGWKDS6NA@mail.gmail.com/3-v4-0002-Framework-for-leader-worker-in-parallel-copy.patch)
download | inline diff:
From eb97b28ecaacfbd248d5662f15bd5e331821a03a Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 08:56:39 +0530
Subject: [PATCH v4 2/6] Framework for leader/worker in parallel copy
This patch has the framework for data structures in parallel copy, leader
initialization, worker initialization, shared memory updation, starting workers,
wait for workers and workers exiting.
---
src/backend/access/transam/parallel.c | 4 +
src/backend/commands/copy.c | 743 +++++++++++++++++++++++++++++++++-
src/include/commands/copy.h | 2 +
src/tools/pgindent/typedefs.list | 7 +
4 files changed, 752 insertions(+), 4 deletions(-)
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index b042696..a3cff4b 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -25,6 +25,7 @@
#include "catalog/pg_enum.h"
#include "catalog/storage.h"
#include "commands/async.h"
+#include "commands/copy.h"
#include "executor/execParallel.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
@@ -145,6 +146,9 @@ static const struct
},
{
"parallel_vacuum_main", parallel_vacuum_main
+ },
+ {
+ "ParallelCopyMain", ParallelCopyMain
}
};
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 436e458..704bbd3 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -96,9 +96,181 @@ typedef enum CopyInsertMethod
} CopyInsertMethod;
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+
+/*
+ * The macros DATA_BLOCK_SIZE, RINGSIZE & MAX_BLOCKS_COUNT stores the records
+ * read from the file that need to be inserted into the relation. These values
+ * help in handover of multiple records with significant size of data to be
+ * processed by each of the workers to make sure there is no context switch & the
+ * work is fairly distributed among the workers. This number showed best
+ * results in the performance tests.
+ */
+#define DATA_BLOCK_SIZE RAW_BUF_SIZE
+
+/* It can hold 1000 blocks of 64K data in DSM to be processed by the worker. */
+#define MAX_BLOCKS_COUNT 1000
+
+/* It can hold upto 10000 record information for worker to process. RINGSIZE
+ * should be a multiple of WORKER_CHUNK_COUNT, as wrap around cases is currently
+ * not handled while selecting the WORKER_CHUNK_COUNT by the worker. */
+#define RINGSIZE (10 * 1000)
+
+/*
+ * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
+ * block to process to avoid lock contention. Read RINGSIZE comments before
+ * changing this value.
+ */
+#define WORKER_CHUNK_COUNT 50
+
+#define IsParallelCopy() (cstate->is_parallel)
+#define IsLeader() (cstate->pcdata->is_leader)
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
+ * Copy data block information.
+ * ParallelCopyDataBlock's will be created in DSM. Data read from file will be
+ * copied in these DSM data blocks. The leader process identifies the records
+ * and the record information will be shared to the workers. The workers will
+ * insert the records into the table. There can be one or more number of records
+ * in each of the data block based on the record size.
+ */
+typedef struct ParallelCopyDataBlock
+{
+ /* The number of unprocessed lines in the current block. */
+ pg_atomic_uint32 unprocessed_line_parts;
+
+ /*
+ * If the current line data is continued into another block, following_block
+ * will have the position where the remaining data need to be read.
+ */
+ uint32 following_block;
+
+ /*
+ * This flag will be set, when the leader finds out this block can be read
+ * safely by the worker. This helps the worker to start processing the line
+ * early where the line will be spread across many blocks and the worker
+ * need not wait for the complete line to be processed.
+ */
+ bool curr_blk_completed;
+ char data[DATA_BLOCK_SIZE]; /* data read from file */
+ uint8 skip_bytes;
+}ParallelCopyDataBlock;
+
+/*
+ * Individual line information.
+ * ParallelCopyLineBoundary is common data structure between leader & worker,
+ * Leader process will be populating data block, data block offset & the size of
+ * the record in DSM for the workers to copy the data into the relation.
+ * This is protected by the following sequence in the leader & worker. If they
+ * don't follow this order the worker might process wrong line_size and leader
+ * might populate the information which worker has not yet processed or in the
+ * process of processing.
+ * Leader should operate in the following order:
+ * 1) check if line_size is -1, if not wait, it means worker is still
+ * processing.
+ * 2) set line_state to LINE_LEADER_POPULATING.
+ * 3) update first_block, start_offset & cur_lineno in any order.
+ * 4) update line_size.
+ * 5) update line_state to LINE_LEADER_POPULATED.
+ * Worker should operate in the following order:
+ * 1) check line_state is LINE_LEADER_POPULATED, if not it means leader is still
+ * populating the data.
+ * 2) read line_size.
+ * 3) only one worker should choose one line for processing, this is handled by
+ * using pg_atomic_compare_exchange_u32, worker will change the state to
+ * LINE_WORKER_PROCESSING only if line_state is LINE_LEADER_POPULATED.
+ * 4) read first_block, start_offset & cur_lineno in any order.
+ * 5) process line_size data.
+ * 6) update line_size to -1.
+ */
+typedef struct ParallelCopyLineBoundary
+{
+ /* Position of the first block in data_blocks array. */
+ uint32 first_block;
+ uint32 start_offset; /* start offset of the line */
+
+ /*
+ * Size of the current line -1 means line is yet to be filled completely,
+ * 0 means empty line, >0 means line filled with line size data.
+ */
+ pg_atomic_uint32 line_size;
+ pg_atomic_uint32 line_state; /* line state */
+ uint64 cur_lineno; /* line number for error messages */
+}ParallelCopyLineBoundary;
+
+/*
+ * Circular queue used to store the line information.
+ */
+typedef struct ParallelCopyLineBoundaries
+{
+ /* Position for the leader to populate a line. */
+ uint32 pos;
+
+ /* Data read from the file/stdin by the leader process. */
+ ParallelCopyLineBoundary ring[RINGSIZE];
+}ParallelCopyLineBoundaries;
+
+/*
+ * Shared information among parallel copy workers. This will be allocated in the
+ * DSM segment.
+ */
+typedef struct ParallelCopyShmInfo
+{
+ bool is_read_in_progress; /* file read status */
+
+ /*
+ * Actual lines inserted by worker, will not be same as
+ * total_worker_processed if where condition is specified along with copy.
+ * This will be the actual records inserted into the relation.
+ */
+ pg_atomic_uint64 processed;
+
+ /*
+ * The number of records currently processed by the worker, this will also
+ * include the number of records that was filtered because of where clause.
+ */
+ pg_atomic_uint64 total_worker_processed;
+ uint64 populated; /* lines populated by leader */
+ uint32 cur_block_pos; /* current data block */
+ ParallelCopyDataBlock data_blocks[MAX_BLOCKS_COUNT]; /* data block array */
+ FullTransactionId full_transaction_id; /* xid for copy from statement */
+ CommandId mycid; /* command id */
+ ParallelCopyLineBoundaries line_boundaries; /* line array */
+} ParallelCopyShmInfo;
+
+/*
+ * Parallel copy line buffer information.
+ */
+typedef struct ParallelCopyLineBuf
+{
+ StringInfoData line_buf;
+ uint64 cur_lineno; /* line number for error messages */
+}ParallelCopyLineBuf;
+
+/*
+ * Parallel copy data information.
+ */
+typedef struct ParallelCopyData
+{
+ Oid relid; /* relation id of the table */
+ ParallelCopyShmInfo *pcshared_info; /* common info in shared memory */
+ bool is_leader;
+
+ /* line position which worker is processing */
+ uint32 worker_processed_pos;
+
+ /*
+ * Local line_buf array, workers will copy it here and release the lines
+ * for the leader to continue.
+ */
+ ParallelCopyLineBuf worker_line_buf[WORKER_CHUNK_COUNT];
+ uint32 worker_line_buf_count; /* Number of lines */
+
+ /* Current position in worker_line_buf */
+ uint32 worker_line_buf_pos;
+}ParallelCopyData;
+
+/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
* even though some fields are used in only some cases.
@@ -230,10 +402,38 @@ typedef struct CopyStateData
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
+ int nworkers;
+ bool is_parallel;
+ ParallelCopyData *pcdata;
/* Shorthand for number of unconsumed bytes available in raw_buf */
#define RAW_BUF_BYTES(cstate) ((cstate)->raw_buf_len - (cstate)->raw_buf_index)
} CopyStateData;
+/*
+ * This structure helps in storing the common data from CopyStateData that are
+ * required by the workers. This information will then be allocated and stored
+ * into the DSM for the worker to retrieve and copy it to CopyStateData.
+ */
+typedef struct SerializedParallelCopyState
+{
+ /* low-level state data */
+ CopyDest copy_dest; /* type of copy source/destination */
+ int file_encoding; /* file or remote side's character encoding */
+ bool need_transcoding; /* file encoding diff from server? */
+ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */
+
+ /* parameters from the COPY command */
+ bool csv_mode; /* Comma Separated Value format? */
+ bool header_line; /* CSV header line? */
+ int null_print_len; /* length of same */
+ bool force_quote_all; /* FORCE_QUOTE *? */
+ bool convert_selectively; /* do selective binary conversion? */
+
+ /* Working state for COPY FROM */
+ AttrNumber num_defaults;
+ Oid relid;
+}SerializedParallelCopyState;
+
/* DestReceiver for COPY (query) TO */
typedef struct
{
@@ -263,6 +463,22 @@ typedef struct
/* Trim the list of buffers back down to this number after flushing */
#define MAX_PARTITION_BUFFERS 32
+/* DSM keys for parallel copy. */
+#define PARALLEL_COPY_KEY_SHARED_INFO 1
+#define PARALLEL_COPY_KEY_CSTATE 2
+#define PARALLEL_COPY_KEY_NULL_PRINT 3
+#define PARALLEL_COPY_KEY_DELIM 4
+#define PARALLEL_COPY_KEY_QUOTE 5
+#define PARALLEL_COPY_KEY_ESCAPE 6
+#define PARALLEL_COPY_KEY_ATTNAME_LIST 7
+#define PARALLEL_COPY_KEY_NOT_NULL_LIST 8
+#define PARALLEL_COPY_KEY_NULL_LIST 9
+#define PARALLEL_COPY_KEY_CONVERT_LIST 10
+#define PARALLEL_COPY_KEY_WHERE_CLAUSE_STR 11
+#define PARALLEL_COPY_KEY_RANGE_TABLE 12
+#define PARALLEL_COPY_WAL_USAGE 13
+#define PARALLEL_COPY_BUFFER_USAGE 14
+
/* Stores multi-insert data related to a single relation in CopyFrom. */
typedef struct CopyMultiInsertBuffer
{
@@ -424,11 +640,477 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
+static pg_attribute_always_inline void EndParallelCopy(ParallelContext *pcxt);
static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
- List *attnamelist);
+ List *attnamelist);
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
+
+/*
+ * SerializeParallelCopyState - Copy shared_cstate using cstate information.
+ */
+static pg_attribute_always_inline void
+SerializeParallelCopyState(CopyState cstate, SerializedParallelCopyState *shared_cstate)
+{
+ shared_cstate->copy_dest = cstate->copy_dest;
+ shared_cstate->file_encoding = cstate->file_encoding;
+ shared_cstate->need_transcoding = cstate->need_transcoding;
+ shared_cstate->encoding_embeds_ascii = cstate->encoding_embeds_ascii;
+ shared_cstate->csv_mode = cstate->csv_mode;
+ shared_cstate->header_line = cstate->header_line;
+ shared_cstate->null_print_len = cstate->null_print_len;
+ shared_cstate->force_quote_all = cstate->force_quote_all;
+ shared_cstate->convert_selectively = cstate->convert_selectively;
+ shared_cstate->num_defaults = cstate->num_defaults;
+ shared_cstate->relid = cstate->pcdata->relid;
+}
+
+/*
+ * RestoreString - Retrieve the string from shared memory.
+ */
+static void
+RestoreString(shm_toc *toc, int sharedkey, char **copystr)
+{
+ char *shared_str_val = (char *)shm_toc_lookup(toc, sharedkey, true);
+ if (shared_str_val)
+ *copystr = pstrdup(shared_str_val);
+}
+
+/*
+ * EstimateLineKeysStr - Estimate the size required in shared memory for the
+ * input string.
+ */
+static void
+EstimateLineKeysStr(ParallelContext *pcxt, char *inputstr)
+{
+ if (inputstr)
+ {
+ shm_toc_estimate_chunk(&pcxt->estimator, strlen(inputstr) + 1);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ }
+}
+
+/*
+ * SerializeString - Insert a string into shared memory.
+ */
+static void
+SerializeString(ParallelContext *pcxt, int key, char *inputstr)
+{
+ if (inputstr)
+ {
+ char *shmptr = (char *)shm_toc_allocate(pcxt->toc,
+ strlen(inputstr) + 1);
+ strcpy(shmptr, inputstr);
+ shm_toc_insert(pcxt->toc, key, shmptr);
+ }
+}
+
+/*
+ * PopulateParallelCopyShmInfo - set ParallelCopyShmInfo.
+ */
+static void
+PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
+ FullTransactionId full_transaction_id)
+{
+ uint32 count;
+
+ MemSet(shared_info_ptr, 0, sizeof(ParallelCopyShmInfo));
+ shared_info_ptr->is_read_in_progress = true;
+ shared_info_ptr->cur_block_pos = -1;
+ shared_info_ptr->full_transaction_id = full_transaction_id;
+ shared_info_ptr->mycid = GetCurrentCommandId(true);
+ for (count = 0; count < RINGSIZE; count++)
+ {
+ ParallelCopyLineBoundary *lineInfo = &shared_info_ptr->line_boundaries.ring[count];
+ pg_atomic_init_u32(&(lineInfo->line_size), -1);
+ }
+}
+
+/*
+ * BeginParallelCopy - start parallel copy tasks.
+ *
+ * Get the number of workers required to perform the parallel copy. The data
+ * structures that are required by the parallel workers will be initialized, the
+ * size required in DSM will be calculated and the necessary keys will be loaded
+ * in the DSM. The specified number of workers will then be launched.
+ */
+static ParallelContext*
+BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
+{
+ ParallelContext *pcxt;
+ ParallelCopyShmInfo *shared_info_ptr;
+ SerializedParallelCopyState *shared_cstate;
+ FullTransactionId full_transaction_id;
+ Size est_cstateshared;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ int parallel_workers = 0;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+ ParallelCopyData *pcdata;
+ MemoryContext oldcontext;
+
+ parallel_workers = Min(nworkers, max_worker_processes);
+
+ /* Can't perform copy in parallel */
+ if (parallel_workers <= 0)
+ return NULL;
+
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ MemoryContextSwitchTo(oldcontext);
+ cstate->pcdata = pcdata;
+
+ EnterParallelMode();
+ pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
+ parallel_workers);
+ Assert(pcxt->nworkers > 0);
+
+ /*
+ * Estimate size for shared information for PARALLEL_COPY_KEY_SHARED_INFO
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(ParallelCopyShmInfo));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_CSTATE */
+ est_cstateshared = MAXALIGN(sizeof(SerializedParallelCopyState));
+ shm_toc_estimate_chunk(&pcxt->estimator, est_cstateshared);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ EstimateLineKeysStr(pcxt, cstate->null_print);
+ EstimateLineKeysStr(pcxt, cstate->null_print_client);
+ EstimateLineKeysStr(pcxt, cstate->delim);
+ EstimateLineKeysStr(pcxt, cstate->quote);
+ EstimateLineKeysStr(pcxt, cstate->escape);
+
+ if (cstate->whereClause != NULL)
+ {
+ whereClauseStr = nodeToString(cstate->whereClause);
+ EstimateLineKeysStr(pcxt, whereClauseStr);
+ }
+
+ if (cstate->range_table != NULL)
+ {
+ rangeTableStr = nodeToString(cstate->range_table);
+ EstimateLineKeysStr(pcxt, rangeTableStr);
+ }
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_XID. */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(FullTransactionId));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_ATTNAME_LIST.
+ */
+ if (attnamelist != NIL)
+ {
+ attnameListStr = nodeToString(attnamelist);
+ EstimateLineKeysStr(pcxt, attnameListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NOT_NULL_LIST.
+ */
+ if (cstate->force_notnull != NIL)
+ {
+ notnullListStr = nodeToString(cstate->force_notnull);
+ EstimateLineKeysStr(pcxt, notnullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NULL_LIST.
+ */
+ if (cstate->force_null != NIL)
+ {
+ nullListStr = nodeToString(cstate->force_null);
+ EstimateLineKeysStr(pcxt, nullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_CONVERT_LIST.
+ */
+ if (cstate->convert_select != NIL)
+ {
+ convertListStr = nodeToString(cstate->convert_select);
+ EstimateLineKeysStr(pcxt, convertListStr);
+ }
+
+ /*
+ * Estimate space for WalUsage and BufferUsage -- PARALLEL_COPY_WAL_USAGE
+ * and PARALLEL_COPY_BUFFER_USAGE.
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ InitializeParallelDSM(pcxt);
+
+ /* If no DSM segment was available, back out (do serial copy) */
+ if (pcxt->seg == NULL)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /* Allocate shared memory for PARALLEL_COPY_KEY_SHARED_INFO */
+ shared_info_ptr = (ParallelCopyShmInfo *) shm_toc_allocate(pcxt->toc, sizeof(ParallelCopyShmInfo));
+ PopulateParallelCopyShmInfo(shared_info_ptr, full_transaction_id);
+
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_SHARED_INFO, shared_info_ptr);
+ pcdata->pcshared_info = shared_info_ptr;
+ pcdata->relid = relid;
+
+ /* Store shared build state, for which we reserved space. */
+ shared_cstate = (SerializedParallelCopyState *)shm_toc_allocate(pcxt->toc, est_cstateshared);
+
+ /* copy cstate variables. */
+ SerializeParallelCopyState(cstate, shared_cstate);
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_CSTATE, shared_cstate);
+
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ATTNAME_LIST, attnameListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NOT_NULL_LIST, notnullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_LIST, nullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_CONVERT_LIST, convertListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, whereClauseStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_RANGE_TABLE, rangeTableStr);
+
+ /*
+ * Allocate space for each worker's WalUsage and BufferUsage; no need to
+ * initialize.
+ */
+ walusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_WAL_USAGE, walusage);
+ bufferusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_BUFFER_USAGE, bufferusage);
+
+ LaunchParallelWorkers(pcxt);
+ if (pcxt->nworkers_launched == 0)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /*
+ * Caller needs to wait for all launched workers when we return. Make sure
+ * that the failure-to-start case will not hang forever.
+ */
+ WaitForParallelWorkersToAttach(pcxt);
+
+ pcdata->is_leader = true;
+ cstate->is_parallel = true;
+ return pcxt;
+}
+
+/*
+ * EndParallelCopy - end the parallel copy tasks.
+ */
+static pg_attribute_always_inline void
+EndParallelCopy(ParallelContext *pcxt)
+{
+ Assert(!IsParallelWorker());
+
+ DestroyParallelContext(pcxt);
+ ExitParallelMode();
+}
+
+/*
+ * InitializeParallelCopyInfo - Initialize parallel worker.
+ */
+static void
+InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
+ CopyState cstate, List *attnamelist)
+{
+ uint32 count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ cstate->copy_dest = shared_cstate->copy_dest;
+ cstate->file_encoding = shared_cstate->file_encoding;
+ cstate->need_transcoding = shared_cstate->need_transcoding;
+ cstate->encoding_embeds_ascii = shared_cstate->encoding_embeds_ascii;
+ cstate->csv_mode = shared_cstate->csv_mode;
+ cstate->header_line = shared_cstate->header_line;
+ cstate->null_print_len = shared_cstate->null_print_len;
+ cstate->force_quote_all = shared_cstate->force_quote_all;
+ cstate->convert_selectively = shared_cstate->convert_selectively;
+ cstate->num_defaults = shared_cstate->num_defaults;
+ pcdata->relid = shared_cstate->relid;
+
+ PopulateCommonCstateInfo(cstate, tup_desc, attnamelist);
+
+ /* Initialize state variables. */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /* Set up variables to avoid per-attribute overhead. */
+ initStringInfo(&cstate->attribute_buf);
+
+ initStringInfo(&cstate->line_buf);
+ for (count = 0; count < WORKER_CHUNK_COUNT; count++)
+ initStringInfo(&pcdata->worker_line_buf[count].line_buf);
+
+ cstate->line_buf_converted = false;
+ cstate->raw_buf = NULL;
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+}
+
+/*
+ * ParallelCopyMain - parallel copy worker's code.
+ *
+ * Where clause handling, convert tuple to columns, add default null values for
+ * the missing columns that are not present in that record. Find the partition
+ * if it is partitioned table, invoke before row insert Triggers, handle
+ * constraints and insert the tuples.
+ */
+void
+ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
+{
+ CopyState cstate;
+ ParallelCopyData *pcdata;
+ ParallelCopyShmInfo *pcshared_info;
+ SerializedParallelCopyState *shared_cstate;
+ Relation rel = NULL;
+ MemoryContext oldcontext;
+ List *attlist = NIL;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+
+ /* Allocate workspace and zero all fields. */
+ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData));
+
+ /*
+ * We allocate everything used by a cstate in a new memory context. This
+ * avoids memory leaks during repeated use of COPY in a query.
+ */
+ cstate->copycontext = AllocSetContextCreate(CurrentMemoryContext,
+ "COPY",
+ ALLOCSET_DEFAULT_SIZES);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ cstate->pcdata = pcdata;
+ pcdata->is_leader = false;
+ pcdata->worker_processed_pos = -1;
+ cstate->is_parallel = true;
+ pcshared_info = (ParallelCopyShmInfo *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_SHARED_INFO, false);
+
+ ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
+
+ pcdata->pcshared_info = pcshared_info;
+
+ shared_cstate = (SerializedParallelCopyState *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
+ cstate->null_print = (char *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_DELIM, &cstate->delim);
+ RestoreString(toc, PARALLEL_COPY_KEY_QUOTE, &cstate->quote);
+ RestoreString(toc, PARALLEL_COPY_KEY_ESCAPE, &cstate->escape);
+ RestoreString(toc, PARALLEL_COPY_KEY_ATTNAME_LIST, &attnameListStr);
+ if (attnameListStr)
+ attlist = (List *)stringToNode(attnameListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NOT_NULL_LIST, ¬nullListStr);
+ if (notnullListStr)
+ cstate->force_notnull = (List *)stringToNode(notnullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NULL_LIST, &nullListStr);
+ if (nullListStr)
+ cstate->force_null = (List *)stringToNode(nullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_CONVERT_LIST, &convertListStr);
+ if (convertListStr)
+ cstate->convert_select = (List *)stringToNode(convertListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, &whereClauseStr);
+ RestoreString(toc, PARALLEL_COPY_KEY_RANGE_TABLE, &rangeTableStr);
+
+ if (whereClauseStr)
+ {
+ Node *whereClauseCnvrtdFrmStr = (Node *) stringToNode(whereClauseStr);
+ cstate->whereClause = whereClauseCnvrtdFrmStr;
+ }
+
+ if (rangeTableStr)
+ {
+ List *rangeTableCnvrtdFrmStr = (List *) stringToNode(rangeTableStr);
+ cstate->range_table = rangeTableCnvrtdFrmStr;
+ }
+
+ /* Open and lock the relation, using the appropriate lock type. */
+ rel = table_open(shared_cstate->relid, RowExclusiveLock);
+ cstate->rel = rel;
+ InitializeParallelCopyInfo(shared_cstate, cstate, attlist);
+
+ CopyFrom(cstate);
+
+ if (rel != NULL)
+ table_close(rel, RowExclusiveLock);
+
+ /* Report WAL/buffer usage during parallel execution */
+ bufferusage = shm_toc_lookup(toc, PARALLEL_COPY_BUFFER_USAGE, false);
+ walusage = shm_toc_lookup(toc, PARALLEL_COPY_WAL_USAGE, false);
+ InstrEndParallelQuery(&bufferusage[ParallelWorkerNumber],
+ &walusage[ParallelWorkerNumber]);
+
+ MemoryContextSwitchTo(oldcontext);
+ pfree(cstate);
+ return;
+}
+
+/*
+ * ParallelCopyFrom - parallel copy leader's functionality.
+ *
+ * Leader executes the before statement for before statement trigger, if before
+ * statement trigger is present. It will read the table data from the file and
+ * copy the contents to DSM data blocks. It will then read the input contents
+ * from the DSM data block and identify the records based on line breaks. This
+ * information is called line or a record that need to be inserted into a
+ * relation. The line information will be stored in ParallelCopyLineBoundary DSM
+ * data structure. Workers will then process this information and insert the
+ * data in to table. It will repeat this process until the all data is read from
+ * the file and all the DSM data blocks are processed. While processing if
+ * leader identifies that DSM Data blocks or DSM ParallelCopyLineBoundary data
+ * structures is full, leader will wait till the worker frees up some entries
+ * and repeat the process. It will wait till all the lines populated are
+ * processed by the workers and exits.
+ */
+static void
+ParallelCopyFrom(CopyState cstate)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+
+ pcshared_info->is_read_in_progress = false;
+ cstate->cur_lineno = 0;
+}
+
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -1141,6 +1823,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
if (is_from)
{
+ ParallelContext *pcxt = NULL;
Assert(rel);
/* check read-only transaction and parallel mode */
@@ -1150,7 +1833,24 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);
cstate->whereClause = whereClause;
- *processed = CopyFrom(cstate); /* copy from file to database */
+ cstate->is_parallel = false;
+
+ if (cstate->nworkers > 0)
+ pcxt = BeginParallelCopy(cstate->nworkers, cstate, stmt->attlist,
+ relid);
+
+ if (pcxt)
+ {
+ ParallelCopyFrom(cstate);
+
+ /* Wait for all copy workers to finish */
+ WaitForParallelWorkersToFinish(pcxt);
+ *processed = pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+ EndParallelCopy(pcxt);
+ }
+ else
+ *processed = CopyFrom(cstate); /* copy from file to database */
+
EndCopyFrom(cstate);
}
else
@@ -1199,6 +1899,7 @@ ProcessCopyOptions(ParseState *pstate,
cstate->is_copy_from = is_from;
cstate->file_encoding = -1;
+ cstate->nworkers = -1;
/* Extract options from the statement node tree */
foreach(option, options)
@@ -1367,6 +2068,39 @@ ProcessCopyOptions(ParseState *pstate,
defel->defname),
parser_errposition(pstate, defel->location)));
}
+ else if (strcmp(defel->defname, "parallel") == 0)
+ {
+ int val;
+ bool parsed;
+ char *strval;
+
+ if (!cstate->is_copy_from)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("parallel option is supported only for copy from"),
+ parser_errposition(pstate, defel->location)));
+ if (cstate->nworkers >= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options"),
+ parser_errposition(pstate, defel->location)));
+
+ strval = defGetString(defel);
+ parsed = parse_int(strval, &val, 0, NULL);
+ if (!parsed)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for integer option \"%s\": %s",
+ defel->defname, strval)));
+ if (val < 1 || val > 1024)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value %s out of bounds for option \"%s\"",
+ strval, defel->defname),
+ errdetail("Valid values are between \"%d\" and \"%d\".",
+ 1, 1024)));
+ cstate->nworkers = val;
+ }
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1720,11 +2454,12 @@ BeginCopy(ParseState *pstate,
/*
* PopulateCommonCstateInfo - Populates the common variables required for copy
- * from operation. This is a helper function for BeginCopy function.
+ * from operation. This is a helper function for BeginCopy &
+ * InitializeParallelCopyInfo function.
*/
static void
PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc,
- List *attnamelist)
+ List *attnamelist)
{
int num_phys_attrs;
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index c639833..82843c6 100644
--- a/src/include/commands/copy.h
+++ b/src/include/commands/copy.h
@@ -14,6 +14,7 @@
#ifndef COPY_H
#define COPY_H
+#include "access/parallel.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
#include "parser/parse_node.h"
@@ -41,4 +42,5 @@ extern uint64 CopyFrom(CopyState cstate);
extern DestReceiver *CreateCopyDestReceiver(void);
+extern void ParallelCopyMain(dsm_segment *seg, shm_toc *toc);
#endif /* COPY_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 3d99046..a0e4ac7 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1700,6 +1700,12 @@ ParallelBitmapHeapState
ParallelBlockTableScanDesc
ParallelCompletionPtr
ParallelContext
+ParallelCopyLineBoundaries
+ParallelCopyLineBoundary
+ParallelCopyData
+ParallelCopyDataBlock
+ParallelCopyLineBuf
+ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
ParallelHashJoinBatch
@@ -2216,6 +2222,7 @@ SerCommitSeqNo
SerialControl
SerializableXactHandle
SerializedActiveRelMaps
+SerializedParallelCopyState
SerializedReindexState
SerializedSnapshotData
SerializedTransactionState
--
1.8.3.1
[text/x-patch] v4-0003-Allow-copy-from-command-to-process-data-from-file.patch (43.1K, ../../CALDaNm2QD5yAsMsgZ-Lr1rqGeCxLcQu7CVrS=Jy3AnGWKDS6NA@mail.gmail.com/4-v4-0003-Allow-copy-from-command-to-process-data-from-file.patch)
download | inline diff:
From ba0bcdcf758d1fb779921b2ab615170a53ecdc4b Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:31:42 +0530
Subject: [PATCH v4 3/6] Allow copy from command to process data from
file/STDIN contents to a table in parallel.
This feature allows the copy from to leverage multiple CPUs in order to copy
data from file/STDIN to a table. This adds a PARALLEL option to COPY FROM
command where the user can specify the number of workers that can be used
to perform the COPY FROM command.
The backend, to which the "COPY FROM" query is submitted acts as leader with
the responsibility of reading data from the file/stdin, launching at most n
number of workers as specified with PARALLEL 'n' option in the "COPY FROM"
query. The leader populates the common data required for the workers execution
in the DSM and shares it with the workers. The leader then executes before
statement triggers if there exists any. Leader populates DSM lines which
includes the start offset and line size, while populating the lines it reads
as many blocks as required into the DSM data blocks from the file. Each block
is of 64K size. The leader parses the data to identify a line, the existing
logic from CopyReadLineText which identifies the lines with some changes was
used for this. Leader checks if a free line is available to copy the
information, if there is no free line it waits till the required line is
freed up by the worker and then copies the identified lines information
(offset & line size) into the DSM lines. This process is repeated till the
complete file is processed. Simultaneously, the workers cache the lines(50)
locally into the local memory and release the lines to the leader for further
populating. Each worker processes the lines that it cached and inserts it into
the table. The leader does not participate in the insertion of data, leaders
only responsibility will be to identify the lines as fast as possible for the
workers to do the actual copy operation. The leader waits till all the lines
populated are processed by the workers and exits.
We have chosen this design based on the reason "that everything stalls if the
leader doesn't accept further input data, as well as when there are no
available splitted chunks so it doesn't seem like a good idea to have the
leader do other work. This is backed by the performance data where we have
seen that with 1 worker there is just a 5-10% performance difference".
---
src/backend/access/common/toast_internals.c | 11 +-
src/backend/access/heap/heapam.c | 13 -
src/backend/access/transam/xact.c | 31 +
src/backend/commands/copy.c | 896 ++++++++++++++++++++++++++--
src/bin/psql/tab-complete.c | 2 +-
src/include/access/xact.h | 2 +
src/tools/pgindent/typedefs.list | 1 +
7 files changed, 902 insertions(+), 54 deletions(-)
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 25a81e5..586d53d 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -17,6 +17,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heaptoast.h"
+#include "access/parallel.h"
#include "access/table.h"
#include "access/toast_internals.h"
#include "access/xact.h"
@@ -116,7 +117,15 @@ toast_save_datum(Relation rel, Datum value,
TupleDesc toasttupDesc;
Datum t_values[3];
bool t_isnull[3];
- CommandId mycid = GetCurrentCommandId(true);
+
+ /*
+ * Parallel copy can insert toast tuples, in case of parallel copy the
+ * command would have been set already by calling AssignCommandIdForWorker.
+ * For parallel copy call GetCurrentCommandId to get currentCommandId by
+ * passing used as false, as this is taken care earlier.
+ */
+ CommandId mycid = IsParallelWorker() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
struct varlena *result;
struct varatt_external toast_pointer;
union
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 8eb276e..a5fa9f5 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2043,19 +2043,6 @@ static HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
- /*
- * Parallel operations are required to be strictly read-only in a parallel
- * worker. Parallel inserts are not safe even in the leader in the
- * general case, because group locking means that heavyweight locks for
- * relation extension or GIN page locks will not conflict between members
- * of a lock group, but we don't prohibit that case here because there are
- * useful special cases that we can safely allow, such as CREATE TABLE AS.
- */
- if (IsParallelWorker())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot insert tuples in a parallel worker")));
-
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index af6afce..e983f78 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -517,6 +517,37 @@ GetCurrentFullTransactionIdIfAny(void)
}
/*
+ * AssignFullTransactionIdForWorker
+ *
+ * For parallel copy, transaction id of leader will be used by the workers.
+ */
+void
+AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId)
+{
+ TransactionState s = CurrentTransactionState;
+
+ Assert((IsInParallelMode() || IsParallelWorker()));
+ s->fullTransactionId = fullTransactionId;
+}
+
+/*
+ * AssignCommandIdForWorker
+ *
+ * For parallel copy, command id of leader will be used by the workers.
+ */
+void
+AssignCommandIdForWorker(CommandId commandId, bool used)
+{
+ Assert((IsInParallelMode() || IsParallelWorker()));
+
+ /* this is global to a transaction, not subtransaction-local */
+ if (used)
+ currentCommandIdUsed = true;
+
+ currentCommandId = commandId;
+}
+
+/*
* MarkCurrentTransactionIdLoggedIfAny
*
* Remember that the current xid - if it is assigned - now has been wal logged.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 704bbd3..868ba4a 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -26,6 +26,7 @@
#include "access/xlog.h"
#include "catalog/dependency.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_proc_d.h"
#include "catalog/pg_type.h"
#include "commands/copy.h"
#include "commands/defrem.h"
@@ -40,11 +41,13 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
+#include "pgstat.h"
#include "port/pg_bswap.h"
#include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
@@ -95,6 +98,18 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+/*
+ * State of the line.
+ */
+typedef enum ParallelCopyLineState
+{
+ LINE_INIT, /* initial state of line */
+ LINE_LEADER_POPULATING, /* leader processing line */
+ LINE_LEADER_POPULATED, /* leader completed populating line */
+ LINE_WORKER_PROCESSING, /* worker processing line */
+ LINE_WORKER_PROCESSED /* worker completed processing line */
+}ParallelCopyLineState;
+
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
/*
@@ -124,6 +139,7 @@ typedef enum CopyInsertMethod
#define IsParallelCopy() (cstate->is_parallel)
#define IsLeader() (cstate->pcdata->is_leader)
+#define IsWorker() (IsParallelCopy() && !IsLeader())
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
@@ -557,9 +573,13 @@ if (1) \
{ \
if (raw_buf_ptr > cstate->raw_buf_index) \
{ \
- appendBinaryStringInfo(&cstate->line_buf, \
- cstate->raw_buf + cstate->raw_buf_index, \
- raw_buf_ptr - cstate->raw_buf_index); \
+ if (!IsParallelCopy()) \
+ appendBinaryStringInfo(&cstate->line_buf, \
+ cstate->raw_buf + cstate->raw_buf_index, \
+ raw_buf_ptr - cstate->raw_buf_index); \
+ else \
+ line_size += raw_buf_ptr - cstate->raw_buf_index; \
+ \
cstate->raw_buf_index = raw_buf_ptr; \
} \
} else ((void) 0)
@@ -572,26 +592,65 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/* Begin parallel copy Macros */
+#define SET_NEWLINE_SIZE() \
+{ \
+ if (cstate->eol_type == EOL_NL || cstate->eol_type == EOL_CR) \
+ new_line_size = 1; \
+ else if (cstate->eol_type == EOL_CRNL) \
+ new_line_size = 2; \
+ else \
+ new_line_size = 0; \
+}
+
+/*
+ * COPY_WAIT_TO_PROCESS - Wait before continuing to process.
+ */
+#define COPY_WAIT_TO_PROCESS() \
+{ \
+ CHECK_FOR_INTERRUPTS(); \
+ (void) WaitLatch(MyLatch, \
+ WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, \
+ 1L, WAIT_EVENT_PG_SLEEP); \
+ ResetLatch(MyLatch); \
+}
+
/*
* CLEAR_EOL_LINE - Wrapper for clearing EOL.
*/
#define CLEAR_EOL_LINE() \
if (!result && !IsHeaderLine()) \
- ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
- cstate->line_buf.len, \
- &cstate->line_buf.len) \
+{ \
+ if (IsParallelCopy()) \
+ ClearEOLFromCopiedData(cstate, cstate->raw_buf, \
+ raw_buf_ptr, &line_size); \
+ else \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len); \
+} \
/*
* INCREMENTPROCESSED - Increment the lines processed.
*/
-#define INCREMENTPROCESSED(processed) \
-processed++;
+#define INCREMENTPROCESSED(processed) \
+{ \
+ if (!IsParallelCopy()) \
+ processed++; \
+ else \
+ pg_atomic_add_fetch_u64(&cstate->pcdata->pcshared_info->processed, 1); \
+}
/*
* RETURNPROCESSED - Get the lines processed.
*/
#define RETURNPROCESSED(processed) \
-return processed;
+if (!IsParallelCopy()) \
+ return processed; \
+else \
+ return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+
+/* End parallel copy Macros */
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -646,7 +705,10 @@ static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
-
+static void ExecBeforeStmtTrigger(CopyState cstate);
+static void CheckTargetRelValidity(CopyState cstate);
+static void PopulateCstateCatalogInfo(CopyState cstate);
+static pg_attribute_always_inline uint32 GetLinePosition(CopyState cstate);
/*
* SerializeParallelCopyState - Copy shared_cstate using cstate information.
*/
@@ -728,6 +790,137 @@ PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
}
/*
+ * IsTriggerFunctionParallelSafe - Check if the trigger function is parallel
+ * safe for the triggers. Return false if any one of the trigger has parallel
+ * unsafe function.
+ */
+static pg_attribute_always_inline bool
+IsTriggerFunctionParallelSafe(TriggerDesc *trigdesc)
+{
+ int i;
+ for (i = 0; i < trigdesc->numtriggers; i++)
+ {
+ Trigger *trigger = &trigdesc->triggers[i];
+ int trigtype = RI_TRIGGER_NONE;
+
+ if (func_parallel(trigger->tgfoid) != PROPARALLEL_SAFE)
+ return false;
+
+ /* If the trigger is parallel safe, also look for RI_TRIGGER. */
+ trigtype = RI_FKey_trigger_type(trigger->tgfoid);
+ if (trigtype == RI_TRIGGER_PK || trigtype == RI_TRIGGER_FK)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * CheckExprParallelSafety - determine parallel safety of volatile expressions
+ * in default clause of column definition or in where clause and return true if
+ * they are parallel safe.
+ */
+static pg_attribute_always_inline bool
+CheckExprParallelSafety(CopyState cstate)
+{
+ if (contain_volatile_functions(cstate->whereClause))
+ {
+ if (max_parallel_hazard((Query *)cstate->whereClause) != PROPARALLEL_SAFE)
+ return false;
+ }
+
+ /*
+ * Check if any of the column has volatile default expression. if yes, and
+ * they are not parallel safe, then parallelism is not allowed. For
+ * instance, if there are any serial/bigserial columns for which nextval()
+ * default expression which is parallel unsafe is associated, parallelism
+ * should not be allowed. In non parallel copy volatile functions are not
+ * checked for nextval().
+ */
+ if (cstate->defexprs != NULL && cstate->num_defaults != 0)
+ {
+ int i;
+ for (i = 0; i < cstate->num_defaults; i++)
+ {
+ bool volatile_expr = contain_volatile_functions((Node *)cstate->defexprs[i]->expr);
+ if (volatile_expr &&
+ (max_parallel_hazard((Query *)cstate->defexprs[i]->expr)) !=
+ PROPARALLEL_SAFE)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * FindInsertMethod - determine insert mode single, multi, or multi conditional.
+ */
+static pg_attribute_always_inline CopyInsertMethod
+FindInsertMethod(CopyState cstate)
+{
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_before_row ||
+ cstate->rel->trigdesc->trig_insert_instead_row))
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+ cstate->rel->trigdesc != NULL &&
+ cstate->rel->trigdesc->trig_insert_new_table)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ return CIM_MULTI_CONDITIONAL;
+
+ return CIM_MULTI;
+}
+
+/*
+ * IsParallelCopyAllowed - check for the cases where parallel copy is not
+ * applicable.
+ */
+static pg_attribute_always_inline bool
+IsParallelCopyAllowed(CopyState cstate)
+{
+ /* Parallel copy not allowed for frontend (2.0 protocol) & binary option. */
+ if ((cstate->copy_dest == COPY_OLD_FE) || cstate->binary)
+ return false;
+
+ /* Check if copy is into foreign table or temporary table. */
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
+ RelationUsesLocalBuffers(cstate->rel))
+ return false;
+
+ /* Check if trigger function is parallel safe. */
+ if (cstate->rel->trigdesc != NULL &&
+ !IsTriggerFunctionParallelSafe(cstate->rel->trigdesc))
+ return false;
+
+ /*
+ * Check if there is after statement or instead of trigger or transition
+ * table triggers.
+ */
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_after_statement ||
+ cstate->rel->trigdesc->trig_insert_instead_row ||
+ cstate->rel->trigdesc->trig_insert_new_table))
+ return false;
+
+ /* Check if the volatile expressions are parallel safe, if present any. */
+ if (!CheckExprParallelSafety(cstate))
+ return false;
+
+ /* Check if the insertion mode is single. */
+ if (FindInsertMethod(cstate) == CIM_SINGLE)
+ return false;
+
+ return true;
+}
+
+/*
* BeginParallelCopy - start parallel copy tasks.
*
* Get the number of workers required to perform the parallel copy. The data
@@ -755,6 +948,7 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
ParallelCopyData *pcdata;
MemoryContext oldcontext;
+ CheckTargetRelValidity(cstate);
parallel_workers = Min(nworkers, max_worker_processes);
/* Can't perform copy in parallel */
@@ -766,6 +960,15 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
MemoryContextSwitchTo(oldcontext);
cstate->pcdata = pcdata;
+ /*
+ * User chosen parallel copy. Determine if the parallel copy is actually
+ * allowed. If not, go with the non-parallel mode.
+ */
+ if (!IsParallelCopyAllowed(cstate))
+ return NULL;
+
+ full_transaction_id = GetCurrentFullTransactionId();
+
EnterParallelMode();
pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
parallel_workers);
@@ -974,9 +1177,214 @@ InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
cstate->line_buf_converted = false;
cstate->raw_buf = NULL;
cstate->raw_buf_index = cstate->raw_buf_len = 0;
+
+ PopulateCstateCatalogInfo(cstate);
+
+ /* Create workspace for CopyReadAttributes results. */
+ if (!cstate->binary)
+ {
+ AttrNumber attr_count = list_length(cstate->attnumlist);
+
+ cstate->max_fields = attr_count;
+ cstate->raw_fields = (char **)palloc(attr_count * sizeof(char *));
+ }
+}
+
+/*
+ * CacheLineInfo - Cache the line information to local memory.
+ */
+static bool
+CacheLineInfo(CopyState cstate, uint32 buff_count)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ uint32 write_pos;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 offset;
+ int dataSize;
+ int copiedSize = 0;
+
+ resetStringInfo(&pcdata->worker_line_buf[buff_count].line_buf);
+ write_pos = GetLinePosition(cstate);
+ if (-1 == write_pos)
+ return true;
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ if (pg_atomic_read_u32(&lineInfo->line_size) == 0)
+ goto empty_data_line_update;
+
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ /* Get the offset information from where the data must be copied. */
+ offset = lineInfo->start_offset;
+ pcdata->worker_line_buf[buff_count].cur_lineno = lineInfo->cur_lineno;
+
+ elog(DEBUG1, "[Worker] Processing - line position:%d, block:%d, unprocessed lines:%d, offset:%d, line size:%d",
+ write_pos, lineInfo->first_block,
+ pg_atomic_read_u32(&data_blk_ptr->unprocessed_line_parts),
+ offset, pg_atomic_read_u32(&lineInfo->line_size));
+
+ for (;;)
+ {
+ uint8 skip_bytes = data_blk_ptr->skip_bytes;
+
+ /*
+ * There is a possibility that the loop embedded at the bottom of the
+ * current loop has come out because data_blk_ptr->curr_blk_completed
+ * is set, but dataSize read might be an old value, if
+ * data_blk_ptr->curr_blk_completed and the line is completed, line_size
+ * will be set. Read the line_size again to be sure if it is completed
+ * or partial block.
+ */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+ if (dataSize)
+ {
+ int remainingSize = dataSize - copiedSize;
+ if (!remainingSize)
+ break;
+
+ /* Whole line is in current block. */
+ if (remainingSize + offset + skip_bytes < DATA_BLOCK_SIZE)
+ {
+ appendBinaryStringInfo(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ remainingSize);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts,
+ 1);
+ break;
+ }
+ else
+ {
+ /* Line is spread across the blocks. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+ while (copiedSize < dataSize)
+ {
+ uint32 currentBlockCopySize;
+ ParallelCopyDataBlock *currBlkPtr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ skip_bytes = currBlkPtr->skip_bytes;
+
+ /*
+ * If complete data is present in current block use
+ * dataSize - copiedSize, or copy the whole block from
+ * current block.
+ */
+ currentBlockCopySize = Min(dataSize - copiedSize, DATA_BLOCK_SIZE - skip_bytes);
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &currBlkPtr->data[0],
+ currentBlockCopySize);
+ pg_atomic_sub_fetch_u32(&currBlkPtr->unprocessed_line_parts, 1);
+ copiedSize += currentBlockCopySize;
+ data_blk_ptr = currBlkPtr;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ /* Copy this complete block from the current offset. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+
+ /*
+ * Reset the offset. For the first copy, copy from the offset. For
+ * the subsequent copy the complete block.
+ */
+ offset = 0;
+
+ /* Set data_blk_ptr to the following block. */
+ data_blk_ptr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ }
+
+ for (;;)
+ {
+ /* Get the size of this line */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ /*
+ * If the data is present in current block lineInfo->line_size
+ * will be updated. If the data is spread across the blocks either
+ * of lineInfo->line_size or data_blk_ptr->curr_blk_completed can
+ * be updated. lineInfo->line_size will be updated if the complete
+ * read is finished. data_blk_ptr->curr_blk_completed will be
+ * updated if processing of current block is finished and data
+ * processing is not finished.
+ */
+ if (data_blk_ptr->curr_blk_completed || (dataSize != -1))
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+ }
+
+empty_data_line_update:
+ elog(DEBUG1, "[Worker] Completed processing line:%d", write_pos);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_WORKER_PROCESSED);
+ pg_atomic_write_u32(&lineInfo->line_size, -1);
+ pg_atomic_add_fetch_u64(&pcshared_info->total_worker_processed, 1);
+ return false;
}
/*
+ * GetWorkerLine - Returns a line for worker to process.
+ */
+static bool
+GetWorkerLine(CopyState cstate)
+{
+ uint32 buff_count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+
+ /*
+ * Copy the line data to line_buf and release the line position so that the
+ * worker can continue loading data.
+ */
+ if (pcdata->worker_line_buf_pos < pcdata->worker_line_buf_count)
+ goto return_line;
+
+ pcdata->worker_line_buf_pos = 0;
+ pcdata->worker_line_buf_count = 0;
+
+ for (buff_count = 0; buff_count < WORKER_CHUNK_COUNT; buff_count++)
+ {
+ bool result = CacheLineInfo(cstate, buff_count);
+ if (result)
+ break;
+
+ pcdata->worker_line_buf_count++;
+ }
+
+ if (pcdata->worker_line_buf_count)
+ goto return_line;
+ else
+ resetStringInfo(&cstate->line_buf);
+
+ return true;
+
+return_line:
+ cstate->line_buf = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].line_buf;
+ cstate->cur_lineno = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].cur_lineno;
+ cstate->line_buf_valid = true;
+
+ /* Mark that encoding conversion hasn't occurred yet. */
+ cstate->line_buf_converted = false;
+ ConvertToServerEncoding(cstate);
+ pcdata->worker_line_buf_pos++;
+ return false;
+ }
+
+/*
* ParallelCopyMain - parallel copy worker's code.
*
* Where clause handling, convert tuple to columns, add default null values for
@@ -1025,6 +1433,8 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
pcdata->pcshared_info = pcshared_info;
+ AssignFullTransactionIdForWorker(pcshared_info->full_transaction_id);
+ AssignCommandIdForWorker(pcshared_info->mycid, true);
shared_cstate = (SerializedParallelCopyState *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
cstate->null_print = (char *)shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
@@ -1085,6 +1495,33 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
}
/*
+ * UpdateBlockInLineInfo - Update the line information.
+ */
+static pg_attribute_always_inline int
+UpdateBlockInLineInfo(CopyState cstate, uint32 blk_pos,
+ uint32 offset, uint32 line_size, uint32 line_state)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ ParallelCopyLineBoundary *lineInfo;
+ int line_pos = lineBoundaryPtr->pos;
+
+ /* Update the line information for the worker to pick and process. */
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ while (pg_atomic_read_u32(&lineInfo->line_size) != -1)
+ COPY_WAIT_TO_PROCESS()
+
+ lineInfo->first_block = blk_pos;
+ lineInfo->start_offset = offset;
+ lineInfo->cur_lineno = cstate->cur_lineno;
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, line_state);
+ lineBoundaryPtr->pos = (lineBoundaryPtr->pos + 1) % RINGSIZE;
+
+ return line_pos;
+}
+
+/*
* ParallelCopyFrom - parallel copy leader's functionality.
*
* Leader executes the before statement for before statement trigger, if before
@@ -1107,8 +1544,302 @@ ParallelCopyFrom(CopyState cstate)
ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+ /* raw_buf is not used in parallel copy, instead data blocks are used.*/
+ pfree(cstate->raw_buf);
+ cstate->raw_buf = NULL;
+
+ /* Execute the before statement triggers from the leader */
+ ExecBeforeStmtTrigger(cstate);
+
+ /* On input just throw the header line away. */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ {
+ pcshared_info->is_read_in_progress = false;
+ return; /* done */
+ }
+ }
+
+ for (;;)
+ {
+ bool done;
+ cstate->cur_lineno++;
+
+ /* Actually read the line into memory here. */
+ done = CopyReadLine(cstate);
+
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ break;
+ }
+
pcshared_info->is_read_in_progress = false;
cstate->cur_lineno = 0;
+ }
+
+/*
+ * GetLinePosition - return the line position that worker should process.
+ */
+static uint32
+GetLinePosition(CopyState cstate)
+{
+ ParallelCopyData *pcdata = cstate->pcdata;
+ ParallelCopyShmInfo *pcshared_info = pcdata->pcshared_info;
+ uint32 previous_pos = pcdata->worker_processed_pos;
+ uint32 write_pos = (previous_pos == -1) ? 0 : (previous_pos + 1) % RINGSIZE;
+ for (;;)
+ {
+ int dataSize;
+ bool is_read_in_progress = pcshared_info->is_read_in_progress;
+ ParallelCopyLineBoundary *lineInfo;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineState line_state = LINE_LEADER_POPULATED;
+ ParallelCopyLineState curr_line_state;
+ CHECK_FOR_INTERRUPTS();
+
+ /* File read completed & no elements to process. */
+ if (!is_read_in_progress &&
+ (pcshared_info->populated ==
+ pg_atomic_read_u64(&pcshared_info->total_worker_processed)))
+ {
+ write_pos = -1;
+ break;
+ }
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ curr_line_state = pg_atomic_read_u32(&lineInfo->line_state);
+ if ((write_pos % WORKER_CHUNK_COUNT == 0) &&
+ (curr_line_state == LINE_WORKER_PROCESSED ||
+ curr_line_state == LINE_WORKER_PROCESSING))
+ {
+ pcdata->worker_processed_pos = write_pos;
+ write_pos = (write_pos + WORKER_CHUNK_COUNT) % RINGSIZE;
+ continue;
+ }
+
+ /* Get the size of this line. */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ if (dataSize != 0) /* If not an empty line. */
+ {
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ if (!data_blk_ptr->curr_blk_completed && (dataSize == -1))
+ {
+ /* Wait till the current line or block is added. */
+ COPY_WAIT_TO_PROCESS()
+ continue;
+ }
+ }
+
+ /* Make sure that no worker has consumed this element. */
+ if (pg_atomic_compare_exchange_u32(&lineInfo->line_state,
+ &line_state, LINE_WORKER_PROCESSING))
+ break;
+ }
+
+ pcdata->worker_processed_pos = write_pos;
+ return write_pos;
+}
+
+/*
+ * GetFreeCopyBlock - Get a free block for data to be copied.
+ */
+static pg_attribute_always_inline uint32
+GetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ int count = 0;
+ uint32 last_free_block = pcshared_info->cur_block_pos;
+ uint32 block_pos = (last_free_block != -1) ? ((last_free_block + 1) % MAX_BLOCKS_COUNT): 0;
+
+ /*
+ * Get a new block for copying data, don't check current block, current
+ * block will have some unprocessed data.
+ */
+ while (count < (MAX_BLOCKS_COUNT - 1))
+ {
+ ParallelCopyDataBlock *dataBlkPtr = &pcshared_info->data_blocks[block_pos];
+ uint32 unprocessed_line_parts = pg_atomic_read_u32(&dataBlkPtr->unprocessed_line_parts);
+ if (unprocessed_line_parts == 0)
+ {
+ dataBlkPtr->curr_blk_completed = false;
+ dataBlkPtr->skip_bytes = 0;
+ pcshared_info->cur_block_pos = block_pos;
+ return block_pos;
+ }
+
+ block_pos = (block_pos + 1) % MAX_BLOCKS_COUNT;
+ count++;
+ }
+
+ return -1;
+}
+
+/*
+ * WaitGetFreeCopyBlock - If there are no blocks available, wait and get a block
+ * for copying data.
+ */
+static uint32
+WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ uint32 new_free_pos = -1;
+ for (;;)
+ {
+ new_free_pos = GetFreeCopyBlock(pcshared_info);
+ if (new_free_pos != -1) /* We have got one block, break now. */
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+
+ return new_free_pos;
+}
+
+/*
+ * SetRawBufForLoad - Set raw_buf to the shared memory where the file data must
+ * be read.
+ */
+static void
+SetRawBufForLoad(CopyState cstate, uint32 line_size, uint32 copy_buf_len,
+ uint32 raw_buf_ptr, char **copy_raw_buf)
+{
+ ParallelCopyShmInfo *pcshared_info;
+ uint32 cur_block_pos;
+ uint32 next_block_pos;
+ ParallelCopyDataBlock *cur_data_blk_ptr = NULL;
+ ParallelCopyDataBlock *next_data_blk_ptr = NULL;
+
+ if (!IsParallelCopy())
+ return;
+
+ pcshared_info = cstate->pcdata->pcshared_info;
+ cur_block_pos = pcshared_info->cur_block_pos;
+ cur_data_blk_ptr = (cstate->raw_buf) ? &pcshared_info->data_blocks[cur_block_pos] : NULL;
+ next_block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ next_data_blk_ptr = &pcshared_info->data_blocks[next_block_pos];
+
+ /* set raw_buf to the data block in shared memory */
+ cstate->raw_buf = next_data_blk_ptr->data;
+ *copy_raw_buf = cstate->raw_buf;
+ if (cur_data_blk_ptr && line_size)
+ {
+ /*
+ * Mark the previous block as completed, worker can start copying this
+ * data.
+ */
+ cur_data_blk_ptr->following_block = next_block_pos;
+ pg_atomic_add_fetch_u32(&cur_data_blk_ptr->unprocessed_line_parts, 1);
+ cur_data_blk_ptr->skip_bytes = copy_buf_len - raw_buf_ptr;
+ cur_data_blk_ptr->curr_blk_completed = true;
+ }
+}
+
+/*
+ * EndLineParallelCopy - Update the line information in shared memory.
+ */
+static void
+EndLineParallelCopy(CopyState cstate, uint32 line_pos, uint32 line_size,
+ uint32 raw_buf_ptr)
+{
+ uint8 new_line_size;
+ if (!IsParallelCopy())
+ return;
+
+ if (!IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ SET_NEWLINE_SIZE()
+ if (line_size)
+ {
+ ParallelCopyLineBoundary *lineInfo = &lineBoundaryPtr->ring[line_pos];
+ /*
+ * If the new_line_size > raw_buf_ptr, then the new block has only
+ * new line char content. The unprocessed count should not be
+ * increased in this case.
+ */
+ if (raw_buf_ptr > new_line_size)
+ {
+ uint32 cur_block_pos = pcshared_info->cur_block_pos;
+ ParallelCopyDataBlock *curr_data_blk_ptr = &pcshared_info->data_blocks[cur_block_pos];
+ pg_atomic_add_fetch_u32(&curr_data_blk_ptr->unprocessed_line_parts, 1);
+ }
+
+ /* Update line size. */
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_LEADER_POPULATED);
+ elog(DEBUG1, "[Leader] After adding - line position:%d, line_size:%d",
+ line_pos, line_size);
+ pcshared_info->populated++;
+ }
+ else if (new_line_size)
+ {
+ /* This means only new line char, empty record should be inserted.*/
+ ParallelCopyLineBoundary *lineInfo;
+ line_pos = UpdateBlockInLineInfo(cstate, -1, -1, 0,
+ LINE_LEADER_POPULATED);
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ elog(DEBUG1, "[Leader] Added empty line with offset:%d, line position:%d, line size:%d",
+ lineInfo->start_offset, line_pos,
+ pg_atomic_read_u32(&lineInfo->line_size));
+ pcshared_info->populated++;
+ }
+ }
+}
+
+/*
+ * ExecBeforeStmtTrigger - Execute the before statement trigger, this will be
+ * executed for parallel copy by the leader process.
+ */
+static void
+ExecBeforeStmtTrigger(CopyState cstate)
+{
+ EState *estate = CreateExecutorState();
+ ResultRelInfo *resultRelInfo;
+
+ Assert(IsLeader());
+
+ /*
+ * We need a ResultRelInfo so we can use the regular executor's
+ * index-entry-making machinery. (There used to be a huge amount of code
+ * here that basically duplicated execUtils.c ...)
+ */
+ resultRelInfo = makeNode(ResultRelInfo);
+ InitResultRelInfo(resultRelInfo,
+ cstate->rel,
+ 1, /* must match rel's position in range_table */
+ NULL,
+ 0);
+
+ /* Verify the named relation is a valid target for INSERT */
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
+
+ estate->es_result_relations = resultRelInfo;
+ estate->es_num_result_relations = 1;
+ estate->es_result_relation_info = resultRelInfo;
+
+ ExecInitRangeTable(estate, cstate->range_table);
+
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+
+ /* Close any trigger target relations */
+ ExecCleanUpTriggerState(estate);
+
+ FreeExecutorState(estate);
}
/*
@@ -3571,7 +4302,8 @@ CopyFrom(CopyState cstate)
PartitionTupleRouting *proute = NULL;
ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
+ CommandId mycid = IsParallelCopy() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
int ti_options = 0; /* start with default options for insert */
BulkInsertState bistate = NULL;
CopyInsertMethod insertMethod;
@@ -3581,7 +4313,14 @@ CopyFrom(CopyState cstate)
bool has_instead_insert_row_trig;
bool leafpart_use_multi_insert = false;
- CheckTargetRelValidity(cstate);
+ /*
+ * Perform this check if it is not parallel copy. In case of parallel
+ * copy, this check is done by the leader, so that if any invalid case
+ * exist the copy from command will error out from the leader itself,
+ * avoiding launching workers, just to throw error.
+ */
+ if (!IsParallelCopy())
+ CheckTargetRelValidity(cstate);
/*
* If the target file is new-in-transaction, we assume that checking FSM
@@ -3621,7 +4360,8 @@ CopyFrom(CopyState cstate)
target_resultRelInfo = resultRelInfo;
/* Verify the named relation is a valid target for INSERT */
- CheckValidResultRel(resultRelInfo, CMD_INSERT);
+ if (!IsParallelCopy())
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
ExecOpenIndices(resultRelInfo, false);
@@ -3770,13 +4510,16 @@ CopyFrom(CopyState cstate)
has_instead_insert_row_trig = (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_instead_row);
- /*
- * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
- * should do this for COPY, since it's not really an "INSERT" statement as
- * such. However, executing these triggers maintains consistency with the
- * EACH ROW triggers that we already fire on COPY.
- */
- ExecBSInsertTriggers(estate, resultRelInfo);
+ if (!IsParallelCopy())
+ {
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+ }
econtext = GetPerTupleExprContext(estate);
@@ -3876,6 +4619,16 @@ CopyFrom(CopyState cstate)
!has_instead_insert_row_trig &&
resultRelInfo->ri_FdwRoutine == NULL;
+ /*
+ * If the table has any partitions that are either foreign or
+ * has BEFORE/INSTEAD OF triggers, we can't perform copy
+ * operations with parallel workers.
+ */
+ if (!leafpart_use_multi_insert && IsParallelWorker())
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot perform PARALLEL COPY if partition has BEFORE/INSTEAD OF triggers, or if the partition is foreign partition"),
+ errhint("Try COPY without PARALLEL option")));
+
/* Set the multi-insert buffer to use for this partition. */
if (leafpart_use_multi_insert)
{
@@ -4292,7 +5045,7 @@ BeginCopyFrom(ParseState *pstate,
* only in text mode.
*/
initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf = (IsParallelCopy()) ? NULL : (char *) palloc(RAW_BUF_SIZE + 1);
cstate->raw_buf_index = cstate->raw_buf_len = 0;
if (!cstate->binary)
{
@@ -4441,26 +5194,35 @@ NextCopyFromRawFields(CopyState cstate, char ***fields, int *nfields)
/* only available for text or csv input */
Assert(!cstate->binary);
- /* on input just throw the header line away */
- if (cstate->cur_lineno == 0 && cstate->header_line)
+ if (IsParallelCopy())
{
- cstate->cur_lineno++;
- if (CopyReadLine(cstate))
- return false; /* done */
+ done = GetWorkerLine(cstate);
+ if (done && cstate->line_buf.len == 0)
+ return false;
}
+ else
+ {
+ /* on input just throw the header line away */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ return false; /* done */
+ }
- cstate->cur_lineno++;
+ cstate->cur_lineno++;
- /* Actually read the line into memory here */
- done = CopyReadLine(cstate);
+ /* Actually read the line into memory here */
+ done = CopyReadLine(cstate);
- /*
- * EOF at start of line means we're done. If we see EOF after some
- * characters, we act as though it was newline followed by EOF, ie,
- * process the line and then exit loop on next iteration.
- */
- if (done && cstate->line_buf.len == 0)
- return false;
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ return false;
+ }
/* Parse the line into de-escaped field values */
if (cstate->csv_mode)
@@ -4708,9 +5470,31 @@ CopyReadLine(CopyState cstate)
*/
if (cstate->copy_dest == COPY_NEW_FE)
{
+ bool bIsFirst = true;
do
{
- cstate->raw_buf_index = cstate->raw_buf_len;
+ if (!IsParallelCopy())
+ cstate->raw_buf_index = cstate->raw_buf_len;
+ else
+ {
+ if (cstate->raw_buf_index == RAW_BUF_SIZE)
+ {
+ /* Get a new block if it is the first time, From the
+ * subsequent time, reset the index and re-use the same
+ * block.
+ */
+ if (bIsFirst)
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ uint32 block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ cstate->raw_buf = pcshared_info->data_blocks[block_pos].data;
+ bIsFirst = false;
+ }
+
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ }
+ }
+
} while (CopyLoadRawBuf(cstate));
}
}
@@ -4765,7 +5549,7 @@ static void
ConvertToServerEncoding(CopyState cstate)
{
/* Done reading the line. Convert it to server encoding. */
- if (cstate->need_transcoding)
+ if (cstate->need_transcoding && (!IsParallelCopy() || IsWorker()))
{
char *cvt;
cvt = pg_any_to_server(cstate->line_buf.data,
@@ -4804,6 +5588,11 @@ CopyReadLineText(CopyState cstate)
char quotec = '\0';
char escapec = '\0';
+ /* For parallel copy */
+ int line_size = 0;
+ int line_pos = 0;
+
+ cstate->eol_type = EOL_UNKNOWN;
if (cstate->csv_mode)
{
quotec = cstate->quote[0];
@@ -4858,6 +5647,8 @@ CopyReadLineText(CopyState cstate)
if (raw_buf_ptr >= copy_buf_len || need_data)
{
REFILL_LINEBUF;
+ SetRawBufForLoad(cstate, line_size, copy_buf_len, raw_buf_ptr,
+ ©_raw_buf);
/*
* Try to read some more data. This will certainly reset
@@ -5082,9 +5873,15 @@ CopyReadLineText(CopyState cstate)
* discard the data and the \. sequence.
*/
if (prev_raw_ptr > cstate->raw_buf_index)
- appendBinaryStringInfo(&cstate->line_buf,
+ {
+ if (!IsParallelCopy())
+ appendBinaryStringInfo(&cstate->line_buf,
cstate->raw_buf + cstate->raw_buf_index,
prev_raw_ptr - cstate->raw_buf_index);
+ else
+ line_size += prev_raw_ptr - cstate->raw_buf_index;
+ }
+
cstate->raw_buf_index = raw_buf_ptr;
result = true; /* report EOF */
break;
@@ -5136,6 +5933,26 @@ not_end_of_copy:
IF_NEED_REFILL_AND_EOF_BREAK(mblen - 1);
raw_buf_ptr += mblen - 1;
}
+
+ /*
+ * Skip the header line. Update the line here, this cannot be done at
+ * the beginning, as there is a possibility that file contains empty
+ * lines.
+ */
+ if (IsParallelCopy() && first_char_in_line && !IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 line_first_block = pcshared_info->cur_block_pos;
+ line_pos = UpdateBlockInLineInfo(cstate,
+ line_first_block,
+ cstate->raw_buf_index, -1,
+ LINE_LEADER_POPULATING);
+ lineInfo = &pcshared_info->line_boundaries.ring[line_pos];
+ elog(DEBUG1, "[Leader] Adding - block:%d, offset:%d, line position:%d",
+ line_first_block, lineInfo->start_offset, line_pos);
+ }
+
first_char_in_line = false;
} /* end of outer loop */
@@ -5144,6 +5961,7 @@ not_end_of_copy:
*/
REFILL_LINEBUF;
CLEAR_EOL_LINE();
+ EndLineParallelCopy(cstate, line_pos, line_size, raw_buf_ptr);
return result;
}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index f41785f..81cc1f4 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2353,7 +2353,7 @@ psql_completion(const char *text, int start, int end)
/* Complete COPY <sth> FROM|TO filename WITH ( */
else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "("))
COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
- "HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
+ "HEADER", "PARALLEL", "QUOTE", "ESCAPE", "FORCE_QUOTE",
"FORCE_NOT_NULL", "FORCE_NULL", "ENCODING");
/* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index df1b43a..71a6c9b 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -385,6 +385,8 @@ extern FullTransactionId GetTopFullTransactionId(void);
extern FullTransactionId GetTopFullTransactionIdIfAny(void);
extern FullTransactionId GetCurrentFullTransactionId(void);
extern FullTransactionId GetCurrentFullTransactionIdIfAny(void);
+extern void AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId);
+extern void AssignCommandIdForWorker(CommandId commandId, bool used);
extern void MarkCurrentTransactionIdLoggedIfAny(void);
extern bool SubTransactionIsActive(SubTransactionId subxid);
extern CommandId GetCurrentCommandId(bool used);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index a0e4ac7..5e5c534 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1705,6 +1705,7 @@ ParallelCopyLineBoundary
ParallelCopyData
ParallelCopyDataBlock
ParallelCopyLineBuf
+ParallelCopyLineState
ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
--
1.8.3.1
[text/x-patch] v4-0004-Documentation-for-parallel-copy.patch (2.0K, ../../CALDaNm2QD5yAsMsgZ-Lr1rqGeCxLcQu7CVrS=Jy3AnGWKDS6NA@mail.gmail.com/5-v4-0004-Documentation-for-parallel-copy.patch)
download | inline diff:
From 65bb658d94e36d3d0f44a19ffaaf60315988c548 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:00:36 +0530
Subject: [PATCH v4 4/6] Documentation for parallel copy.
This patch has the documentation changes for parallel copy.
---
doc/src/sgml/ref/copy.sgml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index 18189ab..2e023ed 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -37,6 +37,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
DELIMITER '<replaceable class="parameter">delimiter_character</replaceable>'
NULL '<replaceable class="parameter">null_string</replaceable>'
HEADER [ <replaceable class="parameter">boolean</replaceable> ]
+ PARALLEL <replaceable class="parameter">integer</replaceable>
QUOTE '<replaceable class="parameter">quote_character</replaceable>'
ESCAPE '<replaceable class="parameter">escape_character</replaceable>'
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
@@ -275,6 +276,22 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
</varlistentry>
<varlistentry>
+ <term><literal>PARALLEL</literal></term>
+ <listitem>
+ <para>
+ Perform <command>COPY FROM</command> in parallel using <replaceable
+ class="parameter"> integer</replaceable> background workers. Please
+ note that it is not guaranteed that the number of parallel workers
+ specified in <replaceable class="parameter">integer</replaceable> will
+ be used during execution. It is possible for a copy to run with fewer
+ workers than specified, or even with no workers at all (for example,
+ due to the setting of max_worker_processes). This option is allowed
+ only in <command>COPY FROM</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>QUOTE</literal></term>
<listitem>
<para>
--
1.8.3.1
[text/x-patch] v4-0005-Tests-for-parallel-copy.patch (19.7K, ../../CALDaNm2QD5yAsMsgZ-Lr1rqGeCxLcQu7CVrS=Jy3AnGWKDS6NA@mail.gmail.com/6-v4-0005-Tests-for-parallel-copy.patch)
download | inline diff:
From ff4f2e8ba3ebf9a6ee25b40450c41c8175ec3e7e Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:19:39 +0530
Subject: [PATCH v4 5/6] Tests for parallel copy.
This patch has the tests for parallel copy.
---
src/test/regress/expected/copy2.out | 205 ++++++++++++++++++++++++++++++++++-
src/test/regress/input/copy.source | 12 +++
src/test/regress/output/copy.source | 12 +++
src/test/regress/sql/copy2.sql | 208 +++++++++++++++++++++++++++++++++++-
4 files changed, 429 insertions(+), 8 deletions(-)
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index e40287d..7ae5d44 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -254,18 +254,32 @@ It is "perfect".|
"It is ""perfect""."," "
"",
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+SELECT * FROM testnl;
+ a | b | c
+---+----------------------+---
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+(2 rows)
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
COPY testeoc TO stdout CSV;
a\.
\.b
c\.d
"\."
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
1 \\0
@@ -280,6 +294,15 @@ SELECT * FROM testnull;
|
(4 rows)
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+SELECT * FROM testnull;
+ a | b
+----+----
+ 42 | \0
+ |
+(2 rows)
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -349,6 +372,34 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ a2
+ b
+(2 rows)
+
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+COMMIT;
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
SELECT * FROM vistest;
a
@@ -409,7 +460,7 @@ SELECT * FROM vistest;
(2 rows)
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -420,6 +471,8 @@ CREATE TEMP TABLE forcetest (
-- should succeed with no effect ("b" remains an empty string, "c" remains NULL)
BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
b | c
@@ -430,6 +483,8 @@ SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
c | d
@@ -486,6 +541,31 @@ select * from check_con_tbl;
(2 rows)
+\d+ check_con_tbl
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | | | plain | |
+Check constraints:
+ "check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
+
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":1}
+NOTICE: input = {"f1":null}
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":0}
+ERROR: new row for relation "check_con_tbl" violates check constraint "check_con_tbl_check"
+DETAIL: Failing row contains (0).
+CONTEXT: COPY check_con_tbl, line 1: "0"
+parallel worker
+select * from check_con_tbl;
+ f1
+----
+ 1
+
+(2 rows)
+
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
CREATE ROLE regress_rls_copy_user_colperms;
@@ -600,8 +680,125 @@ SELECT * FROM instead_of_insert_tbl;
(2 rows)
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+ERROR: value 0 out of bounds for option "parallel"
+DETAIL: Valid values are between "1" and "1024".
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+COPY temp_test (a) from stdin with (PARALLEL 1);
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+ERROR: column "xyz" of relation "test_parallel_copy" does not exist
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+ERROR: column "d" specified more than once
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: invalid input syntax for type integer: ""
+CONTEXT: COPY test_parallel_copy, line 0, column a: ""
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2000 230 23 23"
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2001 231 \N \N"
+parallel worker
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: extra data after last expected column
+CONTEXT: COPY test_parallel_copy, line 1: "2002 232 40 50 60 70 80"
+parallel worker
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ERROR: column "f" does not exist
+LINE 1: ..._parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ERROR: missing FROM-clause entry for table "x"
+LINE 1: ...rallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+ERROR: cannot use subquery in COPY FROM WHERE condition
+LINE 1: ...arallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT...
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+ERROR: set-returning functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...lel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_s...
+ ^
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+ERROR: window functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...rallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number...
+ ^
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+ a | b | c | d | e
+-------+----+------------+---------+---------
+ 1 | 11 | test_c1 | test_d1 | test_e1
+ 2 | 12 | test_c2 | test_d2 | test_e2
+ | 3 | stuff | test_d3 |
+ | 4 | stuff | test_d4 |
+ | 5 | stuff | test_d5 |
+ | | 45 | 80 | 90
+ | | x | \x | \x
+ | | , | \, | \
+ 3000 | | c | |
+ 4000 | | C | |
+ 4001 | 1 | empty | |
+ 4002 | 2 | null | |
+ 4003 | 3 | Backslash | \ | \
+ 4004 | 4 | BackslashX | \X | \X
+ 4005 | 5 | N | N | N
+ 4006 | 6 | BackslashN | \N | \N
+ 4007 | 7 | XX | XX | XX
+ 4008 | 8 | Delimiter | : | :
+ 50004 | 25 | 35 | 45 | 55
+ 60004 | 25 | 35 | 45 | 55
+ 60005 | 26 | 36 | 46 | 56
+(21 rows)
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source
index a1d529a..159c058 100644
--- a/src/test/regress/input/copy.source
+++ b/src/test/regress/input/copy.source
@@ -15,6 +15,13 @@ DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+
+SELECT COUNT(*) FROM tenk1;
+
+TRUNCATE tenk1;
+
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
@@ -159,6 +166,11 @@ truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
+
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source
index 938d355..c3003fe 100644
--- a/src/test/regress/output/copy.source
+++ b/src/test/regress/output/copy.source
@@ -9,6 +9,15 @@ COPY onek FROM '@abs_srcdir@/data/onek.data';
COPY onek TO '@abs_builddir@/results/onek.data';
DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+SELECT COUNT(*) FROM tenk1;
+ count
+-------
+ 10000
+(1 row)
+
+TRUNCATE tenk1;
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
COPY person FROM '@abs_srcdir@/data/person.data';
@@ -113,6 +122,9 @@ insert into parted_copytest select x,1,'One' from generate_series(1011,1020) x;
copy (select * from parted_copytest order by a) to '@abs_builddir@/results/parted_copytest.csv';
truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql
index 902f4fa..7015698 100644
--- a/src/test/regress/sql/copy2.sql
+++ b/src/test/regress/sql/copy2.sql
@@ -157,7 +157,7 @@ COPY y TO stdout (FORMAT CSV, FORCE_QUOTE *);
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
1,"a field with two LFs
@@ -165,8 +165,16 @@ COPY testnl FROM stdin CSV;
inside",2
\.
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+1,"a field with two LFs
+
+inside",2
+\.
+
+SELECT * FROM testnl;
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
a\.
@@ -175,11 +183,19 @@ c\.d
"\."
\.
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+a\.
+\.b
+c\.d
+"\."
+\.
+
COPY testeoc TO stdout CSV;
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
@@ -191,6 +207,14 @@ COPY testnull FROM stdin WITH NULL AS E'\\0';
SELECT * FROM testnull;
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+42 \\0
+\0 \0
+\.
+
+SELECT * FROM testnull;
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -235,6 +259,23 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+a2
+b
+\.
+SELECT * FROM vistest;
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+d2
+e
+\.
+SELECT * FROM vistest;
+COMMIT;
+SELECT * FROM vistest;
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
x
y
@@ -284,7 +325,7 @@ SELECT * FROM vistest;
COMMIT;
SELECT * FROM vistest;
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -297,6 +338,10 @@ BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
1,,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
+1,,""
+\.
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
@@ -304,6 +349,10 @@ BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
2,'a',,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
+2,'a',,""
+\.
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
-- should fail with not-null constraint violation
@@ -339,6 +388,16 @@ copy check_con_tbl from stdin;
0
\.
select * from check_con_tbl;
+\d+ check_con_tbl
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+1
+\N
+\.
+copy check_con_tbl from stdin with (parallel 1);
+0
+\.
+select * from check_con_tbl;
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
@@ -440,8 +499,149 @@ test1
SELECT * FROM instead_of_insert_tbl;
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+1 11 test_c1 test_d1 test_e1
+2 12 test_c2 test_d2 test_e2
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+3 test_d3
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+4 test_d4
+5 test_d5
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+b d
+\.
+
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+10
+\.
+
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+60,60,60
+\.
+
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+60
+\.
+
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+
+COPY temp_test (a) from stdin with (PARALLEL 1);
+10
+\.
+
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2000 230 23 23
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2001 231 \N \N
+\.
+
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2002 232 40 50 60 70 80
+\.
+
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+x,45,80,90
+x,\x,\\x,\\\x
+x,\,,\\\,,\\
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+3000;;c;;
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+4000:\X:C:\X:\X
+4001:1:empty::
+4002:2:null:\X:\X
+4003:3:Backslash:\\:\\
+4004:4:BackslashX:\\X:\\X
+4005:5:N:\N:\N
+4006:6:BackslashN:\\N:\\N
+4007:7:XX:\XX:\XX
+4008:8:Delimiter:\::\:
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+50003 24 34 44 54
+50004 25 35 45 55
+50005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+60001 22 32 42 52
+60002 23 33 43 53
+60003 24 34 44 54
+60004 25 35 45 55
+60005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
--
1.8.3.1
[text/x-patch] v4-0006-Parallel-Copy-For-Binary-Format-Files.patch (27.1K, ../../CALDaNm2QD5yAsMsgZ-Lr1rqGeCxLcQu7CVrS=Jy3AnGWKDS6NA@mail.gmail.com/7-v4-0006-Parallel-Copy-For-Binary-Format-Files.patch)
download | inline diff:
From ccbd0cc315e2733a104dedb984ad5003f74c97e5 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Tue, 18 Aug 2020 16:17:14 +0530
Subject: [PATCH v4 6/6] Parallel Copy For Binary Format Files
Leader reads data from the file into the DSM data blocks each of 64K size.
It also identifies each tuple data block id, start offset, end offset,
tuple size and updates this information in the ring data structure.
Workers parallelly read the tuple information from the ring data structure,
the actual tuple data from the data blocks and parallelly insert the tuples
into the table.
---
src/backend/commands/copy.c | 684 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 599 insertions(+), 85 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 868ba4a..f63dc49 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -264,6 +264,17 @@ typedef struct ParallelCopyLineBuf
}ParallelCopyLineBuf;
/*
+ * Represents the usage mode for CopyReadBinaryGetDataBlock.
+ */
+typedef enum FieldInfoType
+{
+ FIELD_NONE = 0,
+ FIELD_COUNT,
+ FIELD_SIZE,
+ FIELD_DATA
+} FieldInfoType;
+
+/*
* Parallel copy data information.
*/
typedef struct ParallelCopyData
@@ -284,6 +295,9 @@ typedef struct ParallelCopyData
/* Current position in worker_line_buf */
uint32 worker_line_buf_pos;
+
+ /* For binary formatted files */
+ ParallelCopyDataBlock *curr_data_block;
}ParallelCopyData;
/*
@@ -448,6 +462,7 @@ typedef struct SerializedParallelCopyState
/* Working state for COPY FROM */
AttrNumber num_defaults;
Oid relid;
+ bool binary;
}SerializedParallelCopyState;
/* DestReceiver for COPY (query) TO */
@@ -522,7 +537,6 @@ typedef struct CopyMultiInsertInfo
int ti_options; /* table insert options */
} CopyMultiInsertInfo;
-
/*
* These macros centralize code used to process line_buf and raw_buf buffers.
* They are macros because they often do continue/break control and to avoid
@@ -650,11 +664,113 @@ if (!IsParallelCopy()) \
else \
return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+/*
+ * CHECK_FIELD_COUNT - Handles the error cases for field count
+ * for binary format files.
+ */
+#define CHECK_FIELD_COUNT \
+{\
+ if (fld_count == -1) \
+ { \
+ if (IsParallelCopy() && \
+ !IsLeader()) \
+ return true; \
+ else if (IsParallelCopy() && \
+ IsLeader()) \
+ { \
+ if (cstate->pcdata->curr_data_block->data[cstate->raw_buf_index + sizeof(fld_count)] != 0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("received copy data after EOF marker"))); \
+ return true; \
+ } \
+ else \
+ { \
+ /* \
+ * Received EOF marker. In a V3-protocol copy, wait for the \
+ * protocol-level EOF, and complain if it doesn't come \
+ * immediately. This ensures that we correctly handle CopyFail, \
+ * if client chooses to send that now. \
+ * \
+ * Note that we MUST NOT try to read more data in an old-protocol \
+ * copy, since there is no protocol-level EOF marker then. We \
+ * could go either way for copy from file, but choose to throw \
+ * error if there's data after the EOF marker, for consistency \
+ * with the new-protocol case. \
+ */ \
+ char dummy; \
+ if (cstate->copy_dest != COPY_OLD_FE && \
+ CopyReadBinaryData(cstate, &dummy, 1) > 0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("received copy data after EOF marker"))); \
+ return false; \
+ } \
+ } \
+ if (fld_count != cstate->max_fields) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("row field count is %d, expected %d", \
+ (int) fld_count, cstate->max_fields))); \
+}
+
+/*
+ * CHECK_FIELD_SIZE - Handles the error case for field size
+ * for binary format files.
+ */
+#define CHECK_FIELD_SIZE(fld_size) \
+{ \
+ if (fld_size < -1) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("invalid field size")));\
+}
+
+/*
+ * EOF_ERROR - Error statement for EOF for binary format
+ * files.
+ */
+#define EOF_ERROR \
+{ \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("unexpected EOF in COPY data")));\
+}
+
+/*
+ * GET_RAW_BUF_INDEX - Calculates the raw buf index for the cases
+ * where the data spread is across multiple data blocks.
+ */
+#define GET_RAW_BUF_INDEX(raw_buf_index, fld_size, required_blks, curr_blk_bytes) \
+{ \
+ raw_buf_index = fld_size - (((required_blks - 1) * DATA_BLOCK_SIZE) + curr_blk_bytes); \
+}
+
+/*
+ * GET_REQUIRED_BLOCKS - Calculates the number of data
+ * blocks required for the cases where the data spread
+ * is across multiple data blocks.
+ */
+#define GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes) \
+{ \
+ /* \
+ * field size can spread across multiple data blocks, \
+ * calculate the number of required data blocks and try to get \
+ * those many data blocks. \
+ */ \
+ required_blks = (int32)(fld_size - curr_blk_bytes)/(int32)DATA_BLOCK_SIZE; \
+ /* \
+ * check if we need the data block for the field data \
+ * bytes that are not modulus of data block size. \
+ */ \
+ if ((fld_size - curr_blk_bytes)%DATA_BLOCK_SIZE != 0) \
+ required_blks++; \
+}
+
/* End parallel copy Macros */
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
-
/* non-export function prototypes */
static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel,
RawStmt *raw_query, Oid queryRelId, List *attnamelist,
@@ -709,6 +825,14 @@ static void ExecBeforeStmtTrigger(CopyState cstate);
static void CheckTargetRelValidity(CopyState cstate);
static void PopulateCstateCatalogInfo(CopyState cstate);
static pg_attribute_always_inline uint32 GetLinePosition(CopyState cstate);
+static uint32 WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info);
+static bool CopyReadBinaryTupleLeader(CopyState cstate);
+static void CopyReadBinaryFindTupleSize(CopyState cstate, uint32 *line_size);
+static bool CopyReadBinaryTupleWorker(CopyState cstate, Datum *values, bool *nulls);
+static Datum CopyReadBinaryAttributeWorker(CopyState cstate, FmgrInfo *flinfo,
+ Oid typioparam, int32 typmod, bool *isnull);
+static void CopyReadBinaryGetDataBlock(CopyState cstate, FieldInfoType field_info);
+
/*
* SerializeParallelCopyState - Copy shared_cstate using cstate information.
*/
@@ -726,6 +850,7 @@ SerializeParallelCopyState(CopyState cstate, SerializedParallelCopyState *shared
shared_cstate->convert_selectively = cstate->convert_selectively;
shared_cstate->num_defaults = cstate->num_defaults;
shared_cstate->relid = cstate->pcdata->relid;
+ shared_cstate->binary = cstate->binary;
}
/*
@@ -885,8 +1010,8 @@ FindInsertMethod(CopyState cstate)
static pg_attribute_always_inline bool
IsParallelCopyAllowed(CopyState cstate)
{
- /* Parallel copy not allowed for frontend (2.0 protocol) & binary option. */
- if ((cstate->copy_dest == COPY_OLD_FE) || cstate->binary)
+ /* Parallel copy not allowed for frontend (2.0 protocol). */
+ if (cstate->copy_dest == COPY_OLD_FE)
return false;
/* Check if copy is into foreign table or temporary table. */
@@ -1156,6 +1281,7 @@ InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
cstate->convert_selectively = shared_cstate->convert_selectively;
cstate->num_defaults = shared_cstate->num_defaults;
pcdata->relid = shared_cstate->relid;
+ cstate->binary = shared_cstate->binary;
PopulateCommonCstateInfo(cstate, tup_desc, attnamelist);
@@ -1551,32 +1677,66 @@ ParallelCopyFrom(CopyState cstate)
/* Execute the before statement triggers from the leader */
ExecBeforeStmtTrigger(cstate);
- /* On input just throw the header line away. */
- if (cstate->cur_lineno == 0 && cstate->header_line)
+ if (!cstate->binary)
{
- cstate->cur_lineno++;
- if (CopyReadLine(cstate))
+ /* On input just throw the header line away. */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
{
- pcshared_info->is_read_in_progress = false;
- return; /* done */
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ {
+ pcshared_info->is_read_in_progress = false;
+ return; /* done */
+ }
}
- }
- for (;;)
- {
- bool done;
- cstate->cur_lineno++;
+ for (;;)
+ {
+ bool done;
+ cstate->cur_lineno++;
- /* Actually read the line into memory here. */
- done = CopyReadLine(cstate);
+ /* Actually read the line into memory here. */
+ done = CopyReadLine(cstate);
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ break;
+ }
+ }
+ else
+ {
/*
- * EOF at start of line means we're done. If we see EOF after some
- * characters, we act as though it was newline followed by EOF, ie,
- * process the line and then exit loop on next iteration.
+ * Binary Format Files.
+ * For parallel copy leader, fill in the error
+ * context information here, in case any failures
+ * while determining tuple offsets, leader
+ * would throw the errors with proper context.
*/
- if (done && cstate->line_buf.len == 0)
- break;
+ ErrorContextCallback errcallback;
+ errcallback.callback = CopyFromErrorCallback;
+ errcallback.arg = (void *) cstate;
+ errcallback.previous = error_context_stack;
+ error_context_stack = &errcallback;
+ cstate->pcdata->curr_data_block = NULL;
+ cstate->raw_buf_index = 0;
+ pcshared_info->populated = 0;
+ cstate->cur_lineno = 0;
+ cstate->max_fields = list_length(cstate->attnumlist);
+
+ for (;;)
+ {
+ bool eof = false;
+ cstate->cur_lineno++;
+
+ eof = CopyReadBinaryTupleLeader(cstate);
+
+ if (eof)
+ break;
+ }
}
pcshared_info->is_read_in_progress = false;
@@ -1584,7 +1744,355 @@ ParallelCopyFrom(CopyState cstate)
}
/*
- * GetLinePosition - return the line position that worker should process.
+ * CopyReadBinaryGetDataBlock - gets a new block, updates
+ * the current offset, calculates the skip bytes.
+ */
+static void
+CopyReadBinaryGetDataBlock(CopyState cstate, FieldInfoType field_info)
+{
+ ParallelCopyDataBlock *data_block = NULL;
+ ParallelCopyDataBlock *curr_data_block = cstate->pcdata->curr_data_block;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ uint8 move_bytes = 0;
+ uint32 block_pos;
+ uint32 prev_block_pos;
+ int read_bytes = 0;
+
+ prev_block_pos = pcshared_info->cur_block_pos;
+
+ block_pos = WaitGetFreeCopyBlock(pcshared_info);
+
+ if (field_info == FIELD_SIZE || field_info == FIELD_COUNT)
+ move_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+
+ if (curr_data_block != NULL)
+ curr_data_block->skip_bytes = move_bytes;
+
+ data_block = &pcshared_info->data_blocks[block_pos];
+
+ if (move_bytes > 0 && curr_data_block != NULL)
+ memmove(&data_block->data[0], &curr_data_block->data[cstate->raw_buf_index], move_bytes);
+
+ elog(DEBUG1, "LEADER - field info %d is spread across data blocks - moved %d bytes from current block %u to %u block",
+ field_info, move_bytes, prev_block_pos, block_pos);
+
+ read_bytes = CopyGetData(cstate, &data_block->data[move_bytes], 1, (DATA_BLOCK_SIZE - move_bytes));
+
+ if (field_info == FIELD_NONE && cstate->reached_eof)
+ return;
+
+ if (cstate->reached_eof)
+ EOF_ERROR;
+
+ elog(DEBUG1, "LEADER - bytes read from file %d", read_bytes);
+
+ if(field_info == FIELD_SIZE || field_info == FIELD_DATA)
+ {
+ ParallelCopyDataBlock *prev_data_block = NULL;
+ prev_data_block = curr_data_block;
+ prev_data_block->following_block = block_pos;
+
+ if (prev_data_block->curr_blk_completed == false)
+ prev_data_block->curr_blk_completed = true;
+
+ pg_atomic_add_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ }
+
+ cstate->pcdata->curr_data_block = data_block;
+ cstate->raw_buf_index = 0;
+}
+
+/*
+ * CopyReadBinaryTupleLeader - leader reads data from binary formatted file
+ * to data blocks and identifies tuple boundaries/offsets so that workers
+ * can work on the data blocks data.
+ */
+static bool
+CopyReadBinaryTupleLeader(CopyState cstate)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ int16 fld_count;
+ uint32 line_size = 0;
+ uint32 start_block_pos;
+ uint32 start_offset;
+
+ if (cstate->pcdata->curr_data_block == NULL)
+ {
+ CopyReadBinaryGetDataBlock(cstate, FIELD_NONE);
+
+ /*
+ * no data is read from file here. one possibility
+ * to be here could be that the binary file just
+ * has a valid signature but nothing else.
+ */
+ if (cstate->reached_eof)
+ return true;
+ }
+
+ if ((cstate->raw_buf_index + sizeof(fld_count)) >= DATA_BLOCK_SIZE)
+ CopyReadBinaryGetDataBlock(cstate, FIELD_COUNT);
+
+ memcpy(&fld_count, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_count));
+ fld_count = (int16) pg_ntoh16(fld_count);
+ CHECK_FIELD_COUNT;
+ start_offset = cstate->raw_buf_index;
+ cstate->raw_buf_index += sizeof(fld_count);
+ line_size += sizeof(fld_count);
+ start_block_pos = pcshared_info->cur_block_pos;
+
+ CopyReadBinaryFindTupleSize(cstate, &line_size);
+
+ pg_atomic_add_fetch_u32(&cstate->pcdata->curr_data_block->unprocessed_line_parts, 1);
+
+ if (line_size > 0)
+ {
+ int line_pos = UpdateBlockInLineInfo(cstate,
+ start_block_pos,
+ start_offset,
+ line_size,
+ LINE_LEADER_POPULATED);
+
+ pcshared_info->populated++;
+ elog(DEBUG1, "LEADER - adding - block:%u, offset:%u, line size:%u line position:%d",
+ start_block_pos, start_offset, line_size, line_pos);
+ }
+
+ return false;
+}
+
+/*
+ * CopyReadBinaryFindTupleSize - leader identifies boundaries/
+ * offsets for each attribute/column and finally results in the
+ * tuple/row size. It moves on to next data block if the attribute/
+ * column is spread across data blocks.
+ */
+static void
+CopyReadBinaryFindTupleSize(CopyState cstate, uint32 *line_size)
+{
+ int32 fld_size;
+ ListCell *cur;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ foreach(cur, cstate->attnumlist)
+ {
+ int att_num = lfirst_int(cur);
+ Form_pg_attribute att = TupleDescAttr(tup_desc, (att_num - 1));
+ cstate->cur_attname = NameStr(att->attname);
+ fld_size = 0;
+
+ if ((cstate->raw_buf_index + sizeof(fld_size)) >= DATA_BLOCK_SIZE)
+ CopyReadBinaryGetDataBlock(cstate, FIELD_SIZE);
+
+ memcpy(&fld_size, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_size));
+ cstate->raw_buf_index += sizeof(fld_size);
+ *line_size += sizeof(fld_size);
+ fld_size = (int32) pg_ntoh32(fld_size);
+
+ CHECK_FIELD_SIZE(fld_size);
+
+ *line_size += fld_size;
+
+ if ((DATA_BLOCK_SIZE - cstate->raw_buf_index) >= fld_size)
+ {
+ cstate->raw_buf_index += fld_size;
+ elog(DEBUG1, "LEADER - tuple lies in he same data block");
+ }
+ else
+ {
+ int32 required_blks = 0;
+ int32 curr_blk_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+ int i = 0;
+
+ GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes);
+
+ i = required_blks;
+
+ while(i > 0)
+ {
+ CopyReadBinaryGetDataBlock(cstate, FIELD_DATA);
+ i--;
+ }
+
+ GET_RAW_BUF_INDEX(cstate->raw_buf_index, fld_size, required_blks, curr_blk_bytes);
+
+ /*
+ * raw_buf_index should never cross data block size,
+ * as the required number of data blocks would have
+ * been obtained in the above while loop.
+ */
+ Assert(cstate->raw_buf_index <= DATA_BLOCK_SIZE);
+ }
+ cstate->cur_attname = NULL;
+ }
+}
+
+/*
+ * CopyReadBinaryTupleWorker - each worker reads data from data blocks after
+ * getting leader-identified tuple offsets from ring data structure.
+ */
+static bool
+CopyReadBinaryTupleWorker(CopyState cstate, Datum *values, bool *nulls)
+{
+ uint32 line_pos;
+ ParallelCopyLineBoundary *line_info;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ int16 fld_count;
+ ListCell *cur;
+ FmgrInfo *in_functions = cstate->in_functions;
+ Oid *typioparams = cstate->typioparams;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ line_pos = GetLinePosition(cstate);
+
+ if (line_pos == -1)
+ return true;
+
+ line_info = &pcshared_info->line_boundaries.ring[line_pos];
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[line_info->first_block];
+ cstate->raw_buf_index = line_info->start_offset;
+
+ if (cstate->raw_buf_index + sizeof(fld_count) >= DATA_BLOCK_SIZE)
+ {
+ /*
+ * The case where field count spread across datablocks should never occur,
+ * as the leader would have moved it to next block. this code exists for
+ * debugging purposes only.
+ */
+ elog(DEBUG1, "WORKER - field count spread across datablocks should never occur");
+ }
+
+ memcpy(&fld_count, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_count));
+ fld_count = (int16) pg_ntoh16(fld_count);
+
+ CHECK_FIELD_COUNT;
+
+ cstate->raw_buf_index += sizeof(fld_count);
+
+ foreach(cur, cstate->attnumlist)
+ {
+ int att_num = lfirst_int(cur);
+ int m = att_num - 1;
+ Form_pg_attribute att = TupleDescAttr(tup_desc, m);
+ cstate->cur_attname = NameStr(att->attname);
+
+ values[m] = CopyReadBinaryAttributeWorker(cstate,
+ &in_functions[m],
+ typioparams[m],
+ att->atttypmod,
+ &nulls[m]);
+ cstate->cur_attname = NULL;
+ }
+
+ pg_atomic_sub_fetch_u32(&cstate->pcdata->curr_data_block->unprocessed_line_parts, 1);
+ line_info->start_offset = -1;
+ pg_atomic_write_u32(&line_info->line_state, LINE_WORKER_PROCESSED);
+ pg_atomic_write_u32(&line_info->line_size, -1);
+ pg_atomic_add_fetch_u64(&pcshared_info->total_worker_processed, 1);
+
+ return false;
+}
+
+/*
+ * CopyReadBinaryAttributeWorker - leader identifies boundaries/offsets
+ * for each attribute/column, it moves on to next data block if the
+ * attribute/column is spread across data blocks.
+ */
+static Datum
+CopyReadBinaryAttributeWorker(CopyState cstate, FmgrInfo *flinfo,
+ Oid typioparam, int32 typmod, bool *isnull)
+{
+ int32 fld_size;
+ Datum result;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+
+ if ((cstate->raw_buf_index + sizeof(fld_size)) >= DATA_BLOCK_SIZE)
+ {
+ ParallelCopyDataBlock *prev_data_block = cstate->pcdata->curr_data_block;
+ elog(DEBUG1, "WORKER - field size is spread across data blocks");
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[prev_data_block->following_block];
+ pg_atomic_sub_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ cstate->raw_buf_index = 0;
+ }
+
+ memcpy(&fld_size, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_size));
+ fld_size = (int32) pg_ntoh32(fld_size);
+
+ CHECK_FIELD_SIZE(fld_size);
+
+ cstate->raw_buf_index += sizeof(fld_size);
+
+ /* reset attribute_buf to empty, and load raw data in it */
+ resetStringInfo(&cstate->attribute_buf);
+
+ enlargeStringInfo(&cstate->attribute_buf, fld_size);
+
+ if ((DATA_BLOCK_SIZE - cstate->raw_buf_index) >= fld_size)
+ {
+ elog(DEBUG1, "WORKER - tuple lies in single data block");
+ memcpy(&cstate->attribute_buf.data[0],&cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], fld_size);
+ cstate->raw_buf_index += fld_size;
+ }
+ else
+ {
+ uint32 att_buf_idx = 0;
+ uint32 copy_bytes = 0;
+ int32 required_blks = 0;
+ int32 curr_blk_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+ ParallelCopyDataBlock *prev_data_block = NULL;
+ int i = 0;
+
+ GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes);
+
+ i = required_blks;
+ prev_data_block = cstate->pcdata->curr_data_block;
+ elog(DEBUG1, "WORKER - tuple is spread across data blocks");
+ memcpy(&cstate->attribute_buf.data[0], &prev_data_block->data[cstate->raw_buf_index],
+ curr_blk_bytes);
+ copy_bytes = curr_blk_bytes;
+ att_buf_idx = curr_blk_bytes;
+
+ while (i>0)
+ {
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[prev_data_block->following_block];
+ pg_atomic_sub_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ cstate->raw_buf_index = 0;
+ copy_bytes = fld_size - att_buf_idx;
+
+ /*
+ * the bytes that are yet to be taken into att buff are more than
+ * the entire data block size, but only take the data block size
+ * elements.
+ */
+ if (copy_bytes >= DATA_BLOCK_SIZE)
+ copy_bytes = DATA_BLOCK_SIZE;
+
+ memcpy(&cstate->attribute_buf.data[att_buf_idx],
+ &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], copy_bytes);
+ att_buf_idx += copy_bytes;
+ i--;
+ }
+ GET_RAW_BUF_INDEX(cstate->raw_buf_index, fld_size, required_blks, curr_blk_bytes);
+ }
+
+ cstate->attribute_buf.len = fld_size;
+ cstate->attribute_buf.data[fld_size] = '\0';
+
+ /* Call the column type's binary input converter */
+ result = ReceiveFunctionCall(flinfo, &cstate->attribute_buf,
+ typioparam, typmod);
+
+ /* Trouble if it didn't eat the whole buffer */
+ if (cstate->attribute_buf.cursor != cstate->attribute_buf.len)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("incorrect binary data format")));
+
+ *isnull = false;
+ return result;
+}
+
+/*
+ * GetLinePosition - return the line position that worker should pcdata->process.
*/
static uint32
GetLinePosition(CopyState cstate)
@@ -1672,7 +2180,9 @@ GetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
{
dataBlkPtr->curr_blk_completed = false;
dataBlkPtr->skip_bytes = 0;
+ dataBlkPtr->following_block = -1;
pcshared_info->cur_block_pos = block_pos;
+ MemSet(&dataBlkPtr->data[0], 0, DATA_BLOCK_SIZE);
return block_pos;
}
@@ -2188,10 +2698,26 @@ CopyGetInt32(CopyState cstate, int32 *val)
{
uint32 buf;
- if (CopyReadBinaryData(cstate, (char *) &buf, sizeof(buf)) != sizeof(buf))
+ /*
+ * For parallel copy, avoid reading data to raw buf, read directly
+ * from file, later the data will be read to parallel copy data
+ * buffers.
+ */
+ if (cstate->nworkers > 0)
{
- *val = 0; /* suppress compiler warning */
- return false;
+ if (CopyGetData(cstate, &buf, sizeof(buf), sizeof(buf)) != sizeof(buf))
+ {
+ *val = 0; /* suppress compiler warning */
+ return false;
+ }
+ }
+ else
+ {
+ if (CopyReadBinaryData(cstate, (char *) &buf, sizeof(buf)) != sizeof(buf))
+ {
+ *val = 0; /* suppress compiler warning */
+ return false;
+ }
}
*val = (int32) pg_ntoh32(buf);
return true;
@@ -2580,7 +3106,15 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
EndParallelCopy(pcxt);
}
else
+ {
+ /*
+ * Reset nworkers to -1 here. This is useful in cases where user
+ * specifies parallel workers, but, no worker is picked up, so go
+ * back to non parallel mode value of nworkers.
+ */
+ cstate->nworkers = -1;
*processed = CopyFrom(cstate); /* copy from file to database */
+ }
EndCopyFrom(cstate);
}
@@ -5045,7 +5579,7 @@ BeginCopyFrom(ParseState *pstate,
* only in text mode.
*/
initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (IsParallelCopy()) ? NULL : (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
cstate->raw_buf_index = cstate->raw_buf_len = 0;
if (!cstate->binary)
{
@@ -5125,7 +5659,7 @@ BeginCopyFrom(ParseState *pstate,
int32 tmp;
/* Signature */
- if (CopyReadBinaryData(cstate, readSig, 11) != 11 ||
+ if (CopyGetData(cstate, readSig, 11, 11) != 11 ||
memcmp(readSig, BinarySignature, 11) != 0)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
@@ -5153,7 +5687,7 @@ BeginCopyFrom(ParseState *pstate,
/* Skip extension header, if present */
while (tmp-- > 0)
{
- if (CopyReadBinaryData(cstate, readSig, 1) != 1)
+ if (CopyGetData(cstate, readSig, 1, 1) != 1)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("invalid COPY file header (wrong length)")));
@@ -5350,60 +5884,45 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext,
else
{
/* binary */
- int16 fld_count;
- ListCell *cur;
-
cstate->cur_lineno++;
+ cstate->max_fields = list_length(cstate->attnumlist);
- if (!CopyGetInt16(cstate, &fld_count))
+ if (!IsParallelCopy())
{
- /* EOF detected (end of file, or protocol-level EOF) */
- return false;
- }
+ int16 fld_count;
+ ListCell *cur;
- if (fld_count == -1)
- {
- /*
- * Received EOF marker. In a V3-protocol copy, wait for the
- * protocol-level EOF, and complain if it doesn't come
- * immediately. This ensures that we correctly handle CopyFail,
- * if client chooses to send that now.
- *
- * Note that we MUST NOT try to read more data in an old-protocol
- * copy, since there is no protocol-level EOF marker then. We
- * could go either way for copy from file, but choose to throw
- * error if there's data after the EOF marker, for consistency
- * with the new-protocol case.
- */
- char dummy;
-
- if (cstate->copy_dest != COPY_OLD_FE &&
- CopyReadBinaryData(cstate, &dummy, 1) > 0)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("received copy data after EOF marker")));
- return false;
- }
+ if (!CopyGetInt16(cstate, &fld_count))
+ {
+ /* EOF detected (end of file, or protocol-level EOF) */
+ return false;
+ }
- if (fld_count != attr_count)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("row field count is %d, expected %d",
- (int) fld_count, attr_count)));
+ CHECK_FIELD_COUNT;
- foreach(cur, cstate->attnumlist)
+ foreach(cur, cstate->attnumlist)
+ {
+ int attnum = lfirst_int(cur);
+ int m = attnum - 1;
+ Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+
+ cstate->cur_attname = NameStr(att->attname);
+ values[m] = CopyReadBinaryAttribute(cstate,
+ &in_functions[m],
+ typioparams[m],
+ att->atttypmod,
+ &nulls[m]);
+ cstate->cur_attname = NULL;
+ }
+ }
+ else
{
- int attnum = lfirst_int(cur);
- int m = attnum - 1;
- Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+ bool eof = false;
- cstate->cur_attname = NameStr(att->attname);
- values[m] = CopyReadBinaryAttribute(cstate,
- &in_functions[m],
- typioparams[m],
- att->atttypmod,
- &nulls[m]);
- cstate->cur_attname = NULL;
+ eof = CopyReadBinaryTupleWorker(cstate, values, nulls);
+
+ if (eof)
+ return false;
}
}
@@ -6403,18 +6922,15 @@ CopyReadBinaryAttribute(CopyState cstate, FmgrInfo *flinfo,
Datum result;
if (!CopyGetInt32(cstate, &fld_size))
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("unexpected EOF in COPY data")));
+ EOF_ERROR;
+
if (fld_size == -1)
{
*isnull = true;
return ReceiveFunctionCall(flinfo, NULL, typioparam, typmod);
}
- if (fld_size < 0)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("invalid field size")));
+
+ CHECK_FIELD_SIZE(fld_size);
/* reset attribute_buf to empty, and load raw data in it */
resetStringInfo(&cstate->attribute_buf);
@@ -6422,9 +6938,7 @@ CopyReadBinaryAttribute(CopyState cstate, FmgrInfo *flinfo,
enlargeStringInfo(&cstate->attribute_buf, fld_size);
if (CopyReadBinaryData(cstate, cstate->attribute_buf.data,
fld_size) != fld_size)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("unexpected EOF in COPY data")));
+ EOF_ERROR;
cstate->attribute_buf.len = fld_size;
cstate->attribute_buf.data[fld_size] = '\0';
--
1.8.3.1
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-27 02:33 Greg Nancarrow <[email protected]>
parent: vignesh C <[email protected]>
1 sibling, 2 replies; 2431+ messages in thread
From: Greg Nancarrow @ 2020-08-27 02:33 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
> I have attached new set of patches with the fixes.
> Thoughts?
Hi Vignesh,
I don't really have any further comments on the code, but would like
to share some results of some Parallel Copy performance tests I ran
(attached).
The tests loaded a 5GB CSV data file into a 100 column table (of
different data types). The following were varied as part of the test:
- Number of workers (1 – 10)
- No indexes / 4-indexes
- Default settings / increased resources (shared_buffers,work_mem, etc.)
(I did not do any partition-related tests as I believe those type of
tests were previously performed)
I built Postgres (latest OSS code) with the latest Parallel Copy patches (v4).
The test system was a 32-core Intel Xeon E5-4650 server with 378GB of RAM.
I observed the following trends:
- For the data file size used, Parallel Copy achieved best performance
using about 9 – 10 workers. Larger data files may benefit from using
more workers. However, I couldn’t really see any better performance,
for example, from using 16 workers on a 10GB CSV data file compared to
using 8 workers. Results may also vary depending on machine
characteristics.
- Parallel Copy with 1 worker ran slower than normal Copy in a couple
of cases (I did question if allowing 1 worker was useful in my patch
review).
- Typical load time improvement (load factor) for Parallel Copy was
between 2x and 3x. Better load factors can be obtained by using larger
data files and/or more indexes.
- Increasing Postgres resources made little or no difference to
Parallel Copy performance when the target table had no indexes.
Increasing Postgres resources improved Parallel Copy performance when
the target table had indexes.
Regards,
Greg Nancarrow
Fujitsu Australia
(1) Postgres default settings, 5GB CSV (5100000 rows), no indexes on table:
Copy Type Duration (s) Load factor
===============================================
Normal Copy 132.838 -
Parallel Copy
(#workers)
1 97.537 1.36
2 61.700 2.15
3 52.788 2.52
4 46.607 2.85
5 45.524 2.92
6 43.799 3.03
7 42.970 3.09
8 42.974 3.09
9 43.698 3.04
10 43.362 3.06
(2) Postgres default settings, 5GB CSV (5100000 rows), 4 indexes on table:
Copy Type Duration (s) Load factor
===============================================
Normal Copy 221.111 -
Parallel Copy
(#workers)
1 331.609 0.66
2 99.085 2.23
3 89.751 2.46
4 81.137 2.73
5 79.138 2.79
6 77.155 2.87
7 75.813 2.92
8 74.961 2.95
9 77.803 2.84
10 75.399 2.93
(3) Postgres increased resources, 5GB CSV (5100000 rows), no indexes on table:
shared_buffers = 20% of RAM (total RAM = 376GB) = 76GB
work_mem = 10% of RAM = 38GB
maintenance_work_mem = 10% of RAM = 38GB
max_worker_processes = 16
max_parallel_workers = 16
checkpoint_timeout = 30min
max_wal_size=2GB
Copy Type Duration (s) Load factor
===============================================
Normal Copy 78.138 -
Parallel Copy
(#workers)
1 95.203 0.82
2 62.596 1.24
3 52.318 1.49
4 48.246 1.62
5 42.832 1.82
6 42.921 1.82
7 43.146 1.81
8 41.557 1.88
9 43.489 1.80
10 43.362 1.80
(4) Postgres increased resources, 5GB CSV (5100000 rows), 4 indexes on table:
Copy Type Duration (s) Load factor
===============================================
Normal Copy 151.364 -
Parallel Copy
(#workers)
1 120.058 1.26
2 87.465 1.73
3 76.871 1.97
4 69.805 2.17
5 64.100 2.36
6 60.667 2.49
7 59.202 2.56
8 57.417 2.67
9 61.143 2.48
10 57.500 2.63
Attachments:
[text/plain] parallel_copy_perf_summary_5GB_csv.txt (1.9K, ../../CAJcOf-chAO1yjx0W=q+F0VgyCSUcd6VMf9Lb50imuAokQgkU7A@mail.gmail.com/2-parallel_copy_perf_summary_5GB_csv.txt)
download | inline:
(1) Postgres default settings, 5GB CSV (5100000 rows), no indexes on table:
Copy Type Duration (s) Load factor
===============================================
Normal Copy 132.838 -
Parallel Copy
(#workers)
1 97.537 1.36
2 61.700 2.15
3 52.788 2.52
4 46.607 2.85
5 45.524 2.92
6 43.799 3.03
7 42.970 3.09
8 42.974 3.09
9 43.698 3.04
10 43.362 3.06
(2) Postgres default settings, 5GB CSV (5100000 rows), 4 indexes on table:
Copy Type Duration (s) Load factor
===============================================
Normal Copy 221.111 -
Parallel Copy
(#workers)
1 331.609 0.66
2 99.085 2.23
3 89.751 2.46
4 81.137 2.73
5 79.138 2.79
6 77.155 2.87
7 75.813 2.92
8 74.961 2.95
9 77.803 2.84
10 75.399 2.93
(3) Postgres increased resources, 5GB CSV (5100000 rows), no indexes on table:
shared_buffers = 20% of RAM (total RAM = 376GB) = 76GB
work_mem = 10% of RAM = 38GB
maintenance_work_mem = 10% of RAM = 38GB
max_worker_processes = 16
max_parallel_workers = 16
checkpoint_timeout = 30min
max_wal_size=2GB
Copy Type Duration (s) Load factor
===============================================
Normal Copy 78.138 -
Parallel Copy
(#workers)
1 95.203 0.82
2 62.596 1.24
3 52.318 1.49
4 48.246 1.62
5 42.832 1.82
6 42.921 1.82
7 43.146 1.81
8 41.557 1.88
9 43.489 1.80
10 43.362 1.80
(4) Postgres increased resources, 5GB CSV (5100000 rows), 4 indexes on table:
Copy Type Duration (s) Load factor
===============================================
Normal Copy 151.364 -
Parallel Copy
(#workers)
1 120.058 1.26
2 87.465 1.73
3 76.871 1.97
4 69.805 2.17
5 64.100 2.36
6 60.667 2.49
7 59.202 2.56
8 57.417 2.67
9 61.143 2.48
10 57.500 2.63
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-27 02:54 Amit Kapila <[email protected]>
parent: Greg Nancarrow <[email protected]>
1 sibling, 1 reply; 2431+ messages in thread
From: Amit Kapila @ 2020-08-27 02:54 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: vignesh C <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Thu, Aug 27, 2020 at 8:04 AM Greg Nancarrow <[email protected]> wrote:
>
> > I have attached new set of patches with the fixes.
> > Thoughts?
>
> Hi Vignesh,
>
> I don't really have any further comments on the code, but would like
> to share some results of some Parallel Copy performance tests I ran
> (attached).
>
> The tests loaded a 5GB CSV data file into a 100 column table (of
> different data types). The following were varied as part of the test:
> - Number of workers (1 – 10)
> - No indexes / 4-indexes
> - Default settings / increased resources (shared_buffers,work_mem, etc.)
>
> (I did not do any partition-related tests as I believe those type of
> tests were previously performed)
>
> I built Postgres (latest OSS code) with the latest Parallel Copy patches (v4).
> The test system was a 32-core Intel Xeon E5-4650 server with 378GB of RAM.
>
>
> I observed the following trends:
> - For the data file size used, Parallel Copy achieved best performance
> using about 9 – 10 workers. Larger data files may benefit from using
> more workers. However, I couldn’t really see any better performance,
> for example, from using 16 workers on a 10GB CSV data file compared to
> using 8 workers. Results may also vary depending on machine
> characteristics.
> - Parallel Copy with 1 worker ran slower than normal Copy in a couple
> of cases (I did question if allowing 1 worker was useful in my patch
> review).
I think the reason is that for 1 worker case there is not much
parallelization as a leader doesn't perform the actual load work.
Vignesh, can you please once see if the results are reproducible at
your end, if so, we can once compare the perf profiles to see why in
some cases we get improvement and in other cases not. Based on that we
can decide whether to allow the 1 worker case or not.
> - Typical load time improvement (load factor) for Parallel Copy was
> between 2x and 3x. Better load factors can be obtained by using larger
> data files and/or more indexes.
>
Nice improvement and I think you are right that with larger load data
we will get even better improvement.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-27 11:26 vignesh C <[email protected]>
parent: Amit Kapila <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: vignesh C @ 2020-08-27 11:26 UTC (permalink / raw)
To: Amit Kapila <[email protected]>; +Cc: Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Thu, Aug 27, 2020 at 8:24 AM Amit Kapila <[email protected]> wrote:
>
> On Thu, Aug 27, 2020 at 8:04 AM Greg Nancarrow <[email protected]> wrote:
> >
> > > I have attached new set of patches with the fixes.
> > > Thoughts?
> >
> > Hi Vignesh,
> >
> > I don't really have any further comments on the code, but would like
> > to share some results of some Parallel Copy performance tests I ran
> > (attached).
> >
> > The tests loaded a 5GB CSV data file into a 100 column table (of
> > different data types). The following were varied as part of the test:
> > - Number of workers (1 – 10)
> > - No indexes / 4-indexes
> > - Default settings / increased resources (shared_buffers,work_mem, etc.)
> >
> > (I did not do any partition-related tests as I believe those type of
> > tests were previously performed)
> >
> > I built Postgres (latest OSS code) with the latest Parallel Copy patches (v4).
> > The test system was a 32-core Intel Xeon E5-4650 server with 378GB of RAM.
> >
> >
> > I observed the following trends:
> > - For the data file size used, Parallel Copy achieved best performance
> > using about 9 – 10 workers. Larger data files may benefit from using
> > more workers. However, I couldn’t really see any better performance,
> > for example, from using 16 workers on a 10GB CSV data file compared to
> > using 8 workers. Results may also vary depending on machine
> > characteristics.
> > - Parallel Copy with 1 worker ran slower than normal Copy in a couple
> > of cases (I did question if allowing 1 worker was useful in my patch
> > review).
>
> I think the reason is that for 1 worker case there is not much
> parallelization as a leader doesn't perform the actual load work.
> Vignesh, can you please once see if the results are reproducible at
> your end, if so, we can once compare the perf profiles to see why in
> some cases we get improvement and in other cases not. Based on that we
> can decide whether to allow the 1 worker case or not.
>
I will spend some time on this and update.
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-27 12:12 Amit Kapila <[email protected]>
parent: vignesh C <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Amit Kapila @ 2020-08-27 12:12 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Thu, Aug 27, 2020 at 4:56 PM vignesh C <[email protected]> wrote:
>
> On Thu, Aug 27, 2020 at 8:24 AM Amit Kapila <[email protected]> wrote:
> >
> > On Thu, Aug 27, 2020 at 8:04 AM Greg Nancarrow <[email protected]> wrote:
> > >
> > > > I have attached new set of patches with the fixes.
> > > > Thoughts?
> > >
> > > Hi Vignesh,
> > >
> > > I don't really have any further comments on the code, but would like
> > > to share some results of some Parallel Copy performance tests I ran
> > > (attached).
> > >
> > > The tests loaded a 5GB CSV data file into a 100 column table (of
> > > different data types). The following were varied as part of the test:
> > > - Number of workers (1 – 10)
> > > - No indexes / 4-indexes
> > > - Default settings / increased resources (shared_buffers,work_mem, etc.)
> > >
> > > (I did not do any partition-related tests as I believe those type of
> > > tests were previously performed)
> > >
> > > I built Postgres (latest OSS code) with the latest Parallel Copy patches (v4).
> > > The test system was a 32-core Intel Xeon E5-4650 server with 378GB of RAM.
> > >
> > >
> > > I observed the following trends:
> > > - For the data file size used, Parallel Copy achieved best performance
> > > using about 9 – 10 workers. Larger data files may benefit from using
> > > more workers. However, I couldn’t really see any better performance,
> > > for example, from using 16 workers on a 10GB CSV data file compared to
> > > using 8 workers. Results may also vary depending on machine
> > > characteristics.
> > > - Parallel Copy with 1 worker ran slower than normal Copy in a couple
> > > of cases (I did question if allowing 1 worker was useful in my patch
> > > review).
> >
> > I think the reason is that for 1 worker case there is not much
> > parallelization as a leader doesn't perform the actual load work.
> > Vignesh, can you please once see if the results are reproducible at
> > your end, if so, we can once compare the perf profiles to see why in
> > some cases we get improvement and in other cases not. Based on that we
> > can decide whether to allow the 1 worker case or not.
> >
>
> I will spend some time on this and update.
>
Thanks.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-08-31 10:43 vignesh C <[email protected]>
parent: Greg Nancarrow <[email protected]>
1 sibling, 1 reply; 2431+ messages in thread
From: vignesh C @ 2020-08-31 10:43 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Thu, Aug 27, 2020 at 8:04 AM Greg Nancarrow <[email protected]> wrote:
> - Parallel Copy with 1 worker ran slower than normal Copy in a couple
> of cases (I did question if allowing 1 worker was useful in my patch
> review).
Thanks Greg for your review & testing.
I had executed various tests with 1GB, 2GB & 5GB with 100 columns without
parallel mode & with 1 parallel worker. Test result for the same is as
given below:
Test Without parallel mode With 1 Parallel worker
1GB csv file 100 columns
(100 bytes data in each column) 62 seconds 47 seconds (1.32X)
1GB csv file 100 columns
(1000 bytes data in each column) 89 seconds 78 seconds (1.14X)
2GB csv file 100 columns
(1 byte data in each column) 277 seconds 256 seconds (1.08X)
5GB csv file 100 columns
(100 byte data in each column) 515 seconds 445 seconds (1.16X)
I have run the tests multiple times and have noticed the similar execution
times in all the runs for the above tests.
In the above results there is slight improvement with 1 worker. In my tests
I did not observe the degradation for copy with 1 worker compared to the
non parallel copy. Can you share with me the script you used to generate
the data & the ddl of the table, so that it will help me check that
scenario you faced the problem.
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-15 13:49 Bharath Rupireddy <[email protected]>
parent: vignesh C <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Bharath Rupireddy @ 2020-09-15 13:49 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: vignesh C <[email protected]>; PostgreSQL Hackers <[email protected]>
On Fri, Sep 11, 2020 at 3:49 AM Greg Nancarrow <[email protected]> wrote:
>
> I couldn't use the original machine from which I obtained the previous
> results, but ended up using a 4-core CentOS7 VM, which showed a
> similar pattern in the performance results for this test case.
> I obtained the following results from loading a 2GB CSV file (1000000
> rows, 4 indexes):
>
> Copy Type Duration (s) Load factor
> ===============================================
> Normal Copy 190.891 -
>
> Parallel Copy
> (#workers)
> 1 210.947 0.90
>
Hi Greg,
I tried to recreate the test case(attached) and I didn't find much
difference with the custom postgresql.config file.
Test case: 250000 tuples, 4 indexes(composite indexes with 10
columns), 3.7GB, 100 columns(as suggested by you and all the
varchar(255) columns are having 255 characters), exec time in sec.
With custom postgresql.conf[1], removed and recreated the data
directory after every run(I couldn't perform the OS page cache flush
due to some reasons. So, chose this recreation of data dir way, for
testing purpose):
HEAD: 129.547, 128.624, 128.890
Patch: 0 workers - 130.213, 131.298, 130.555
Patch: 1 worker - 127.757, 125.560, 128.275
With default postgresql.conf, removed and recreated the data directory
after every run:
HEAD: 138.276, 150.472, 153.304
Patch: 0 workers - 162.468, 149.423, 159.137
Patch: 1 worker - 136.055, 144.250, 137.916
Few questions:
1. Was the run performed with default postgresql.conf file? If not,
what are the changed configurations?
2. Are the readings for normal copy(190.891sec, mentioned by you
above) taken on HEAD or with patch, 0 workers? How much is the runtime
with your test case on HEAD(Without patch) and 0 workers(With patch)?
3. Was the run performed on release build?
4. Were the readings taken on multiple runs(say 3 or 4 times)?
[1] - Postgres configuration used for above testing:
shared_buffers = 40GB
max_worker_processes = 32
max_parallel_maintenance_workers = 24
max_parallel_workers = 32
synchronous_commit = off
checkpoint_timeout = 1d
max_wal_size = 24GB
min_wal_size = 15GB
autovacuum = off
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
Attachments:
[text/rtf] testcase.rtf (11.9K, ../../CALj2ACUYWf+53rythiVZG6oVm0v5y=ftvouq13JtVqhk7GPMkQ@mail.gmail.com/2-testcase.rtf)
download | inline:
{\rtf1\ansi\ansicpg1252\cocoartf2513
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\margl1440\margr1440\vieww30560\viewh11000\viewkind0
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
\f0\fs24 \cf0 DROP TABLE IF EXISTS t100_1;\
CREATE TABLE t100_1 (\
c1_s_1 bigserial,\
c2_vc_1 varchar(255),\
c3_d_1 date,\
c4_n_1 numeric,\
c5_vc_2 varchar(255),\
c6_d_2 date,\
c7_vc_3 varchar(255),\
c8_t_1 time without time zone,\
c9_vc_4 varchar(255),\
c10_vc_5 varchar(255),\
c11_t_2 time without time zone,\
c12_vc_6 varchar(255),\
c13_vc_7 varchar(255),\
c14_n_2 numeric,\
c15_d_3 date,\
c16_t_3 time without time zone,\
c17_n_3 numeric,\
c18_vc_8 varchar(255),\
c19_d_4 date,\
c20_vc_9 varchar(255),\
c21_t_4 time without time zone,\
c22_vc_10 varchar(255),\
c23_d_5 date,\
c24_vc_11 varchar(255),\
c25_t_5 time without time zone,\
c26_t_6 time without time zone,\
c27_vc_12 varchar(255),\
c28_vc_13 varchar(255),\
c29_d_6 date,\
c30_vc_14 varchar(255),\
c31_vc_15 varchar(255),\
c32_t_7 time without time zone,\
c33_t_8 time without time zone,\
c34_vc_16 varchar(255),\
c35_vc_17 varchar(255),\
c36_d_7 date,\
c37_d_8 date,\
c38_vc_18 varchar(255),\
c39_t_9 time without time zone,\
c40_vc_19 varchar(255),\
c41_vc_20 varchar(255),\
c42_vc_21 varchar(255),\
c43_vc_22 varchar(255),\
c44_t_10 time without time zone,\
c45_d_9 date,\
c46_vc_23 varchar(255),\
c47_t_11 time without time zone,\
c48_vc_24 varchar(255),\
c49_vc_25 varchar(255),\
c50_vc_26 varchar(255),\
c51_d_10 date,\
c52_vc_27 varchar(255),\
c53_vc_28 varchar(255),\
c54_vc_29 varchar(255),\
c55_vc_30 varchar(255),\
c56_d_11 date,\
c57_vc_31 varchar(255),\
c58_vc_32 varchar(255),\
c59_vc_33 varchar(255),\
c60_vc_34 varchar(255),\
c61_vc_35 varchar(255),\
c62_vc_36 varchar(255),\
c63_vc_37 varchar(255),\
c64_vc_38 varchar(255),\
c65_vc_39 varchar(255),\
c66_n_4 numeric,\
c67_vc_40 varchar(255),\
c68_vc_41 varchar(255),\
c69_vc_42 varchar(255),\
c70_vc_43 varchar(255),\
c71_n_5 numeric,\
c72_vc_44 varchar(255),\
c73_n_6 numeric,\
c74_vc_45 varchar(255),\
c75_vc_46 varchar(255),\
c76_vc_47 varchar(255),\
c77_n_7 numeric,\
c78_n_8 numeric,\
c79_d_12 date,\
c80_n_9 numeric,\
c81_vc_48 varchar(255),\
c82_vc_49 varchar(255),\
c83_vc_50 varchar(255),\
c84_n_10 numeric,\
c85_vc_51 varchar(255),\
c86_vc_52 varchar(255),\
c87_vc_53 varchar(255),\
c88_d_13 date,\
c89_n_13 numeric,\
c90_vc_54 varchar(255),\
c91_vc_55 varchar(255),\
c92_n_11 numeric,\
c93_n_12 numeric,\
c94_vc_56 varchar(255),\
c95_t_12 time without time zone,\
c96_vc_57 varchar(255),\
c97_vc_58 varchar(255),\
c98_vc_59 varchar(255),\
c99_vc_60 varchar(255),\
c100_t_13 time without time zone\
);\
\
\
INSERT INTO t100_1\
SELECT i,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date,\
log(i),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
clock_timestamp()::time without time zone,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
clock_timestamp()::time without time zone,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
log(i),\
current_date, \
clock_timestamp()::time without time zone, \
log(i), \
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
clock_timestamp()::time without time zone, \
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
clock_timestamp()::time without time zone, \
clock_timestamp()::time without time zone, \
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date, \
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date,\
clock_timestamp()::time without time zone,\
clock_timestamp()::time without time zone,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date,\
current_date,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
clock_timestamp()::time without time zone,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
clock_timestamp()::time without time zone,\
current_date,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
clock_timestamp()::time without time zone,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date,\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
log(i),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
log(i),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
log(i),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
log(i),\
log(i),\
current_date,\
log(i),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
log(i),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
current_date,\
log(i),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
log(i),\
log(i),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
clock_timestamp()::time without time zone, \
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||md5(i::text)||left(md5(i::text), 31),\
clock_timestamp()::time without time zone \
FROM generate_series(1, 250000, 1) AS i;\
\
\
CREATE INDEX t100_1_idx_1 ON t100_2(c1_s_1, c5_vc_2, c25_t_5, c35_vc_17, c37_d_8, c66_n_4, c70_vc_43, c87_vc_53, c88_d_13, c90_vc_54);\
CREATE INDEX t100_1_idx_2 ON t100_2(c1_s_1, c95_t_12, c91_vc_55, c85_vc_51, c78_n_8, c77_n_7, c70_vc_43, c36_d_7, c16_t_3, c13_vc_7);\
CREATE INDEX t100_1_idx_3 ON t100_2(c1_s_1, c25_t_5, c9_vc_4, c3_d_1, c13_vc_7, c57_vc_31, c58_vc_32, c61_vc_35, c69_vc_42, c72_vc_44);\
CREATE INDEX t100_1_idx_4 ON t100_2(c1_s_1, c45_d_9, c71_n_5, c63_vc_37, c66_n_4, c44_t_10, c20_vc_9, c65_vc_39, c61_vc_35, c75_vc_46);\
\
COPY t100_2 FROM '/mnt/ssd/bharath.rupireddy/pc0.25mn.csv' WITH(FORMAT CSV);\
\
COPY t100_2 FROM '/mnt/ssd/bharath.rupireddy/pc0.25mn.csv' WITH(FORMAT CSV, PARALLEL 1);\
}
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-16 07:50 Greg Nancarrow <[email protected]>
parent: Bharath Rupireddy <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Greg Nancarrow @ 2020-09-16 07:50 UTC (permalink / raw)
To: Bharath Rupireddy <[email protected]>; +Cc: vignesh C <[email protected]>; PostgreSQL Hackers <[email protected]>
Hi Bharath,
On Tue, Sep 15, 2020 at 11:49 PM Bharath Rupireddy
<[email protected]> wrote:
>
> Few questions:
> 1. Was the run performed with default postgresql.conf file? If not,
> what are the changed configurations?
Yes, just default settings.
> 2. Are the readings for normal copy(190.891sec, mentioned by you
> above) taken on HEAD or with patch, 0 workers?
With patch
>How much is the runtime
> with your test case on HEAD(Without patch) and 0 workers(With patch)?
TBH, I didn't test that. Looking at the changes, I wouldn't expect a
degradation of performance for normal copy (you have tested, right?).
> 3. Was the run performed on release build?
For generating the perf data I sent (normal copy vs parallel copy with
1 worker), I used a debug build (-g -O0), as that is needed for
generating all the relevant perf data for Postgres code. Previously I
ran with a release build (-O2).
> 4. Were the readings taken on multiple runs(say 3 or 4 times)?
The readings I sent were from just one run (not averaged), but I did
run the tests several times to verify the readings were representative
of the pattern I was seeing.
Fortunately I have been given permission to share the exact table
definition and data I used, so you can check the behaviour and timings
on your own test machine.
Please see the attachment.
You can create the table using the table.sql and index_4.sql
definitions in the "sql" directory.
The data.csv file (to be loaded by COPY) can be created with the
included "dupdata" tool in the "input" directory, which you need to
build, then run, specifying a suitable number of records and path of
the template record (see README). Obviously the larger the number of
records, the larger the file ...
The table can then be loaded using COPY with "format csv" (and
"parallel N" if testing parallel copy).
Regards,
Greg Nancarrow
Fujitsu Australia
Attachments:
[application/zip] table_data_generation_files_to_share.zip (4.6K, ../../CAJcOf-dUchi35jTZu7Qdjs9P6=u3t73oLsLXSiW6EqK0=eY6dg@mail.gmail.com/2-table_data_generation_files_to_share.zip)
download
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-16 13:05 Ashutosh Sharma <[email protected]>
parent: vignesh C <[email protected]>
1 sibling, 1 reply; 2431+ messages in thread
From: Ashutosh Sharma @ 2020-09-16 13:05 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
Hi Vignesh,
I've spent some time today looking at your new set of patches and I've
some thoughts and queries which I would like to put here:
Why are these not part of the shared cstate structure?
SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
I think in the refactoring patch we could replace all the cstate
variables that would be shared between the leader and workers with a
common structure which would be used even for a serial copy. Thoughts?
--
Have you tested your patch when encoding conversion is needed? If so,
could you please point out the email that has the test results.
--
Apart from above, I've noticed some cosmetic errors which I am sharing here:
+#define IsParallelCopy() (cstate->is_parallel)
+#define IsLeader() (cstate->pcdata->is_leader)
This doesn't look to be properly aligned.
--
+ shared_info_ptr = (ParallelCopyShmInfo *)
shm_toc_allocate(pcxt->toc, sizeof(ParallelCopyShmInfo));
+ PopulateParallelCopyShmInfo(shared_info_ptr, full_transaction_id);
..
+ /* Store shared build state, for which we reserved space. */
+ shared_cstate = (SerializedParallelCopyState
*)shm_toc_allocate(pcxt->toc, est_cstateshared);
In the first case, while typecasting you've added a space between the
typename and the function but that is missing in the second case. I
think it would be good if you could make it consistent.
Same comment applies here as well:
+ pg_atomic_uint32 line_state; /* line state */
+ uint64 cur_lineno; /* line number for error messages */
+}ParallelCopyLineBoundary;
...
+ CommandId mycid; /* command id */
+ ParallelCopyLineBoundaries line_boundaries; /* line array */
+} ParallelCopyShmInfo;
There is no space between the closing brace and the structure name in
the first case but it is in the second one. So, again this doesn't
look consistent.
I could also find this type of inconsistency in comments. See below:
+/* It can hold upto 10000 record information for worker to process. RINGSIZE
+ * should be a multiple of WORKER_CHUNK_COUNT, as wrap around cases
is currently
+ * not handled while selecting the WORKER_CHUNK_COUNT by the worker. */
+#define RINGSIZE (10 * 1000)
...
+/*
+ * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
+ * block to process to avoid lock contention. Read RINGSIZE comments before
+ * changing this value.
+ */
+#define WORKER_CHUNK_COUNT 50
You may see these kinds of errors at other places as well if you scan
through your patch.
--
With Regards,
Ashutosh Sharma
EnterpriseDB:http://www.enterprisedb.com
On Wed, Aug 19, 2020 at 11:51 AM vignesh C <[email protected]> wrote:
>
> Thanks Greg for reviewing the patch. Please find my thoughts for your comments.
>
> On Mon, Aug 17, 2020 at 9:44 AM Greg Nancarrow <[email protected]> wrote:
> > Some further comments:
> >
> > (1) v3-0002-Framework-for-leader-worker-in-parallel-copy.patch
> >
> > +/*
> > + * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
> > + * block to process to avoid lock contention. This value should be divisible by
> > + * RINGSIZE, as wrap around cases is currently not handled while selecting the
> > + * WORKER_CHUNK_COUNT by the worker.
> > + */
> > +#define WORKER_CHUNK_COUNT 50
> >
> >
> > "This value should be divisible by RINGSIZE" is not a correct
> > statement (since obviously 50 is not divisible by 10000).
> > It should say something like "This value should evenly divide into
> > RINGSIZE", or "RINGSIZE should be a multiple of WORKER_CHUNK_COUNT".
> >
>
> Fixed. Changed it to RINGSIZE should be a multiple of WORKER_CHUNK_COUNT.
>
> > (2) v3-0003-Allow-copy-from-command-to-process-data-from-file.patch
> >
> > (i)
> >
> > + /*
> > + * If the data is present in current block
> > lineInfo. line_size
> > + * will be updated. If the data is spread
> > across the blocks either
> >
> > Somehow a space has been put between "lineinfo." and "line_size".
> > It should be: "If the data is present in current block
> > lineInfo.line_size will be updated"
>
> Fixed, changed it to lineinfo->line_size.
>
> >
> > (ii)
> >
> > >This is not possible because of pg_atomic_compare_exchange_u32, this
> > >will succeed only for one of the workers whose line_state is
> > >LINE_LEADER_POPULATED, for other workers it will fail. This is
> > >explained in detail above ParallelCopyLineBoundary.
> >
> > Yes, but prior to that call to pg_atomic_compare_exchange_u32(),
> > aren't you separately reading line_state and line_state, so that
> > between those reads, it may have transitioned from leader to another
> > worker, such that the read line state ("cur_line_state", being checked
> > in the if block) may not actually match what is now in the line_state
> > and/or the read line_size ("dataSize") doesn't actually correspond to
> > the read line state?
> >
> > (sorry, still not 100% convinced that the synchronization and checks
> > are safe in all cases)
> >
>
> I think that you are describing about the problem could happen in the
> following case:
> when we read curr_line_state, the value was LINE_WORKER_PROCESSED or
> LINE_WORKER_PROCESSING. Then in some cases if the leader is very fast
> compared to the workers then the leader quickly populates one line and
> sets the state to LINE_LEADER_POPULATED. State is changed to
> LINE_LEADER_POPULATED when we are checking the currr_line_state.
> I feel this will not be a problem because, Leader will populate & wait
> till some RING element is available to populate. In the meantime
> worker has seen that state is LINE_WORKER_PROCESSED or
> LINE_WORKER_PROCESSING(previous state that it read), worker has
> identified that this chunk was processed by some other worker, worker
> will move and try to get the next available chunk & insert those
> records. It will keep continuing till it gets the next chunk to
> process. Eventually one of the workers will get this chunk and process
> it.
>
> > (3) v3-0006-Parallel-Copy-For-Binary-Format-Files.patch
> >
> > >raw_buf is not used in parallel copy, instead raw_buf will be pointing
> > >to shared memory data blocks. This memory was allocated as part of
> > >BeginCopyFrom, uptil this point we cannot be 100% sure as copy can be
> > >performed sequentially like in case max_worker_processes is not
> > >available, if it switches to sequential mode raw_buf will be used
> > >while performing copy operation. At this place we can safely free this
> > >memory that was allocated
> >
> > So the following code (which checks raw_buf, which still points to
> > memory that has been pfreed) is still valid?
> >
> > In the SetRawBufForLoad() function, which is called by CopyReadLineText():
> >
> > cur_data_blk_ptr = (cstate->raw_buf) ?
> > &pcshared_info->data_blocks[cur_block_pos] : NULL;
> >
> > The above code looks a bit dicey to me. I stepped over that line in
> > the debugger when I debugged an instance of Parallel Copy, so it
> > definitely gets executed.
> > It makes me wonder what other code could possibly be checking raw_buf
> > and using it in some way, when in fact what it points to has been
> > pfreed.
> >
> > Are you able to add the following line of code, or will it (somehow)
> > break logic that you are relying on?
> >
> > pfree(cstate->raw_buf);
> > cstate->raw_buf = NULL; <=== I suggest that this line is added
> >
>
> You are right, I have debugged & verified it sets it to an invalid
> block which is not expected. There are chances this would have caused
> some corruption in some machines. The suggested fix is required, I
> have fixed it. I have moved this change to
> 0003-Allow-copy-from-command-to-process-data-from-file.patch as
> 0006-Parallel-Copy-For-Binary-Format-Files is only for Binary format
> parallel copy & that change is common change for parallel copy.
>
> I have attached new set of patches with the fixes.
> Thoughts?
>
> Regards,
> Vignesh
> EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-17 05:36 Bharath Rupireddy <[email protected]>
parent: Greg Nancarrow <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Bharath Rupireddy @ 2020-09-17 05:36 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: vignesh C <[email protected]>; PostgreSQL Hackers <[email protected]>
On Wed, Sep 16, 2020 at 1:20 PM Greg Nancarrow <[email protected]> wrote:
>
> Fortunately I have been given permission to share the exact table
> definition and data I used, so you can check the behaviour and timings
> on your own test machine.
>
Thanks Greg for the script. I ran your test case and I didn't observe
any increase in exec time with 1 worker, indeed, we have benefitted a
few seconds from 0 to 1 worker as expected.
Execution time is in seconds. Each test case is executed 3 times on
release build. Each time the data directory is recreated.
Case 1: 1000000 rows, 2GB
With Patch, default configuration, 0 worker: 88.933, 92.261, 88.423
With Patch, default configuration, 1 worker: 73.825, 74.583, 72.678
With Patch, custom configuration, 0 worker: 76.191, 78.160, 78.822
With Patch, custom configuration, 1 worker: 61.289, 61.288, 60.573
Case 2: 2550000 rows, 5GB
With Patch, default configuration, 0 worker: 246.031, 188.323, 216.683
With Patch, default configuration, 1 worker: 156.299, 153.293, 170.307
With Patch, custom configuration, 0 worker: 197.234, 195.866, 196.049
With Patch, custom configuration, 1 worker: 157.173, 158.287, 157.090
[1] - Custom configuration is set up to ensure that no other processes
influence the results. The postgresql.conf used:
shared_buffers = 40GB
synchronous_commit = off
checkpoint_timeout = 1d
max_wal_size = 24GB
min_wal_size = 15GB
autovacuum = off
max_worker_processes = 32
max_parallel_maintenance_workers = 24
max_parallel_workers = 32
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-22 09:14 vignesh C <[email protected]>
parent: Ashutosh Sharma <[email protected]>
0 siblings, 3 replies; 2431+ messages in thread
From: vignesh C @ 2020-09-22 09:14 UTC (permalink / raw)
To: Ashutosh Sharma <[email protected]>; +Cc: Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
Thanks Ashutosh for your comments.
On Wed, Sep 16, 2020 at 6:36 PM Ashutosh Sharma <[email protected]> wrote:
>
> Hi Vignesh,
>
> I've spent some time today looking at your new set of patches and I've
> some thoughts and queries which I would like to put here:
>
> Why are these not part of the shared cstate structure?
>
> SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
> SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
> SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
> SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
>
I have used shared_cstate mainly to share the integer & bool data
types from the leader to worker process. The above data types are of
char* data type, I will not be able to use it like how I could do it
for integer type. So I preferred to send these as separate keys to the
worker. Thoughts?
> I think in the refactoring patch we could replace all the cstate
> variables that would be shared between the leader and workers with a
> common structure which would be used even for a serial copy. Thoughts?
>
Currently we are using shared_cstate only to share integer & bool data
types from leader to worker. Once worker retrieves the shared data for
integer & bool data types, worker will copy it to cstate. I preferred
this way because only for integer & bool we retrieve to shared_cstate
& copy it to cstate and for rest of the members any way we are
directly copying back to cstate. Thoughts?
> Have you tested your patch when encoding conversion is needed? If so,
> could you please point out the email that has the test results.
>
We have not yet done encoding testing, we will do and post the results
separately in the coming days.
> Apart from above, I've noticed some cosmetic errors which I am sharing here:
>
> +#define IsParallelCopy() (cstate->is_parallel)
> +#define IsLeader() (cstate->pcdata->is_leader)
>
> This doesn't look to be properly aligned.
>
Fixed.
> + shared_info_ptr = (ParallelCopyShmInfo *)
> shm_toc_allocate(pcxt->toc, sizeof(ParallelCopyShmInfo));
> + PopulateParallelCopyShmInfo(shared_info_ptr, full_transaction_id);
>
> ..
>
> + /* Store shared build state, for which we reserved space. */
> + shared_cstate = (SerializedParallelCopyState
> *)shm_toc_allocate(pcxt->toc, est_cstateshared);
>
> In the first case, while typecasting you've added a space between the
> typename and the function but that is missing in the second case. I
> think it would be good if you could make it consistent.
>
Fixed
> Same comment applies here as well:
>
> + pg_atomic_uint32 line_state; /* line state */
> + uint64 cur_lineno; /* line number for error messages */
> +}ParallelCopyLineBoundary;
>
> ...
>
> + CommandId mycid; /* command id */
> + ParallelCopyLineBoundaries line_boundaries; /* line array */
> +} ParallelCopyShmInfo;
>
> There is no space between the closing brace and the structure name in
> the first case but it is in the second one. So, again this doesn't
> look consistent.
>
Fixed
> I could also find this type of inconsistency in comments. See below:
>
> +/* It can hold upto 10000 record information for worker to process. RINGSIZE
> + * should be a multiple of WORKER_CHUNK_COUNT, as wrap around cases
> is currently
> + * not handled while selecting the WORKER_CHUNK_COUNT by the worker. */
> +#define RINGSIZE (10 * 1000)
>
> ...
>
> +/*
> + * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
> + * block to process to avoid lock contention. Read RINGSIZE comments before
> + * changing this value.
> + */
> +#define WORKER_CHUNK_COUNT 50
>
> You may see these kinds of errors at other places as well if you scan
> through your patch.
Fixed.
Please find the attached v5 patch which has the fixes for the same.
Thoughts?
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
Attachments:
[text/x-patch] v5-0001-Copy-code-readjustment-to-support-parallel-copy.patch (16.6K, ../../CALDaNm2rRNnLoC0g9z9aDzpz1FoAanv5u00ri1d+E8Y3of5rxg@mail.gmail.com/2-v5-0001-Copy-code-readjustment-to-support-parallel-copy.patch)
download | inline diff:
From 443297e9b1f842a78cdd37e6f273dbfa7a706897 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 08:52:48 +0530
Subject: [PATCH v5 1/6] Copy code readjustment to support parallel copy.
This patch has the copy code slightly readjusted so that the common code is
separated to functions/macros, these functions/macros will be used by the
workers in the parallel copy code of the upcoming patches. EOL removal is moved
from CopyReadLine to CopyReadLineText, this change was required because in case
of parallel copy the record identification and record updation is done in
CopyReadLineText, before record information is updated in shared memory the new
line characters should be removed.
---
src/backend/commands/copy.c | 360 ++++++++++++++++++++++++++------------------
1 file changed, 217 insertions(+), 143 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 2047557..cf7277a 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -95,6 +95,9 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
+
/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
@@ -224,7 +227,6 @@ typedef struct CopyStateData
* appropriate amounts of data from this buffer. In both modes, we
* guarantee that there is a \0 at raw_buf[raw_buf_len].
*/
-#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
@@ -354,6 +356,27 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/*
+ * CLEAR_EOL_LINE - Wrapper for clearing EOL.
+ */
+#define CLEAR_EOL_LINE() \
+if (!result && !IsHeaderLine()) \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len) \
+
+/*
+ * INCREMENTPROCESSED - Increment the lines processed.
+ */
+#define INCREMENTPROCESSED(processed) \
+processed++;
+
+/*
+ * RETURNPROCESSED - Get the lines processed.
+ */
+#define RETURNPROCESSED(processed) \
+return processed;
+
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -401,7 +424,11 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
-
+static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
+ List *attnamelist);
+static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size);
+static void ConvertToServerEncoding(CopyState cstate);
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -801,14 +828,18 @@ CopyLoadRawBuf(CopyState cstate)
{
int nbytes = RAW_BUF_BYTES(cstate);
int inbytes;
+ int minread = 1;
/* Copy down the unprocessed data if any. */
if (nbytes > 0)
memmove(cstate->raw_buf, cstate->raw_buf + cstate->raw_buf_index,
nbytes);
+ if (cstate->copy_dest == COPY_NEW_FE)
+ minread = RAW_BUF_SIZE - nbytes;
+
inbytes = CopyGetData(cstate, cstate->raw_buf + nbytes,
- 1, RAW_BUF_SIZE - nbytes);
+ minread, RAW_BUF_SIZE - nbytes);
nbytes += inbytes;
cstate->raw_buf[nbytes] = '\0';
cstate->raw_buf_index = 0;
@@ -1514,7 +1545,6 @@ BeginCopy(ParseState *pstate,
{
CopyState cstate;
TupleDesc tupDesc;
- int num_phys_attrs;
MemoryContext oldcontext;
/* Allocate workspace and zero all fields */
@@ -1680,6 +1710,23 @@ BeginCopy(ParseState *pstate,
tupDesc = cstate->queryDesc->tupDesc;
}
+ PopulateCommonCstateInfo(cstate, tupDesc, attnamelist);
+ cstate->copy_dest = COPY_FILE; /* default */
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return cstate;
+}
+
+/*
+ * PopulateCommonCstateInfo - Populates the common variables required for copy
+ * from operation. This is a helper function for BeginCopy function.
+ */
+static void
+PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc, List *attnamelist)
+{
+ int num_phys_attrs;
+
/* Generate or convert list of attributes to process */
cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
@@ -1799,12 +1846,6 @@ BeginCopy(ParseState *pstate,
pg_database_encoding_max_length() > 1);
/* See Multibyte encoding comment above */
cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->file_encoding);
-
- cstate->copy_dest = COPY_FILE; /* default */
-
- MemoryContextSwitchTo(oldcontext);
-
- return cstate;
}
/*
@@ -2696,32 +2737,11 @@ CopyMultiInsertInfoStore(CopyMultiInsertInfo *miinfo, ResultRelInfo *rri,
}
/*
- * Copy FROM file to relation.
+ * Check if the relation specified in copy from is valid.
*/
-uint64
-CopyFrom(CopyState cstate)
+static void
+CheckTargetRelValidity(CopyState cstate)
{
- ResultRelInfo *resultRelInfo;
- ResultRelInfo *target_resultRelInfo;
- ResultRelInfo *prevResultRelInfo = NULL;
- EState *estate = CreateExecutorState(); /* for ExecConstraints() */
- ModifyTableState *mtstate;
- ExprContext *econtext;
- TupleTableSlot *singleslot = NULL;
- MemoryContext oldcontext = CurrentMemoryContext;
-
- PartitionTupleRouting *proute = NULL;
- ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
- int ti_options = 0; /* start with default options for insert */
- BulkInsertState bistate = NULL;
- CopyInsertMethod insertMethod;
- CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
- uint64 processed = 0;
- bool has_before_insert_row_trig;
- bool has_instead_insert_row_trig;
- bool leafpart_use_multi_insert = false;
-
Assert(cstate->rel);
/*
@@ -2758,27 +2778,6 @@ CopyFrom(CopyState cstate)
RelationGetRelationName(cstate->rel))));
}
- /*
- * If the target file is new-in-transaction, we assume that checking FSM
- * for free space is a waste of time. This could possibly be wrong, but
- * it's unlikely.
- */
- if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
- (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
- cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
- ti_options |= TABLE_INSERT_SKIP_FSM;
-
- /*
- * Optimize if new relfilenode was created in this subxact or one of its
- * committed children and we won't see those rows later as part of an
- * earlier scan or command. The subxact test ensures that if this subxact
- * aborts then the frozen rows won't be visible after xact cleanup. Note
- * that the stronger test of exactly which subtransaction created it is
- * crucial for correctness of this optimization. The test for an earlier
- * scan or command tolerates false negatives. FREEZE causes other sessions
- * to see rows they would not see under MVCC, and a false negative merely
- * spreads that anomaly to the current session.
- */
if (cstate->freeze)
{
/*
@@ -2816,9 +2815,61 @@ CopyFrom(CopyState cstate)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot perform COPY FREEZE because the table was not created or truncated in the current subtransaction")));
+ }
+}
+
+/*
+ * Copy FROM file to relation.
+ */
+uint64
+CopyFrom(CopyState cstate)
+{
+ ResultRelInfo *resultRelInfo;
+ ResultRelInfo *target_resultRelInfo;
+ ResultRelInfo *prevResultRelInfo = NULL;
+ EState *estate = CreateExecutorState(); /* for ExecConstraints() */
+ ModifyTableState *mtstate;
+ ExprContext *econtext;
+ TupleTableSlot *singleslot = NULL;
+ MemoryContext oldcontext = CurrentMemoryContext;
+
+ PartitionTupleRouting *proute = NULL;
+ ErrorContextCallback errcallback;
+ CommandId mycid = GetCurrentCommandId(true);
+ int ti_options = 0; /* start with default options for insert */
+ BulkInsertState bistate = NULL;
+ CopyInsertMethod insertMethod;
+ CopyMultiInsertInfo multiInsertInfo = {0}; /* pacify compiler */
+ uint64 processed = 0;
+ bool has_before_insert_row_trig;
+ bool has_instead_insert_row_trig;
+ bool leafpart_use_multi_insert = false;
+
+ CheckTargetRelValidity(cstate);
+ /*
+ * If the target file is new-in-transaction, we assume that checking FSM
+ * for free space is a waste of time. This could possibly be wrong, but
+ * it's unlikely.
+ */
+ if (RELKIND_HAS_STORAGE(cstate->rel->rd_rel->relkind) &&
+ (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
+ cstate->rel->rd_firstRelfilenodeSubid != InvalidSubTransactionId))
+ ti_options |= TABLE_INSERT_SKIP_FSM;
+
+ /*
+ * Optimize if new relfilenode was created in this subxact or one of its
+ * committed children and we won't see those rows later as part of an
+ * earlier scan or command. The subxact test ensures that if this subxact
+ * aborts then the frozen rows won't be visible after xact cleanup. Note
+ * that the stronger test of exactly which subtransaction created it is
+ * crucial for correctness of this optimization. The test for an earlier
+ * scan or command tolerates false negatives. FREEZE causes other sessions
+ * to see rows they would not see under MVCC, and a false negative merely
+ * spreads that anomaly to the current session.
+ */
+ if (cstate->freeze)
ti_options |= TABLE_INSERT_FROZEN;
- }
/*
* We need a ResultRelInfo so we can use the regular executor's
@@ -3311,7 +3362,7 @@ CopyFrom(CopyState cstate)
* or FDW; this is the same definition used by nodeModifyTable.c
* for counting tuples inserted by an INSERT command.
*/
- processed++;
+ INCREMENTPROCESSED(processed)
}
}
@@ -3366,30 +3417,15 @@ CopyFrom(CopyState cstate)
FreeExecutorState(estate);
- return processed;
+ RETURNPROCESSED(processed)
}
/*
- * Setup to read tuples from a file for COPY FROM.
- *
- * 'rel': Used as a template for the tuples
- * 'filename': Name of server-local file to read
- * 'attnamelist': List of char *, columns to include. NIL selects all cols.
- * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
- *
- * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ * PopulateCstateCatalogInfo - Populate the catalog information.
*/
-CopyState
-BeginCopyFrom(ParseState *pstate,
- Relation rel,
- const char *filename,
- bool is_program,
- copy_data_source_cb data_source_cb,
- List *attnamelist,
- List *options)
+static void
+PopulateCstateCatalogInfo(CopyState cstate)
{
- CopyState cstate;
- bool pipe = (filename == NULL);
TupleDesc tupDesc;
AttrNumber num_phys_attrs,
num_defaults;
@@ -3399,38 +3435,8 @@ BeginCopyFrom(ParseState *pstate,
Oid in_func_oid;
int *defmap;
ExprState **defexprs;
- MemoryContext oldcontext;
bool volatile_defexprs;
- cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
- oldcontext = MemoryContextSwitchTo(cstate->copycontext);
-
- /* Initialize state variables */
- cstate->reached_eof = false;
- cstate->eol_type = EOL_UNKNOWN;
- cstate->cur_relname = RelationGetRelationName(cstate->rel);
- cstate->cur_lineno = 0;
- cstate->cur_attname = NULL;
- cstate->cur_attval = NULL;
-
- /*
- * Set up variables to avoid per-attribute overhead. attribute_buf and
- * raw_buf are used in both text and binary modes, but we use line_buf
- * only in text mode.
- */
- initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
- cstate->raw_buf_index = cstate->raw_buf_len = 0;
- if (!cstate->binary)
- {
- initStringInfo(&cstate->line_buf);
- cstate->line_buf_converted = false;
- }
-
- /* Assign range table, we'll need it in CopyFrom. */
- if (pstate)
- cstate->range_table = pstate->p_rtable;
-
tupDesc = RelationGetDescr(cstate->rel);
num_phys_attrs = tupDesc->natts;
num_defaults = 0;
@@ -3508,6 +3514,61 @@ BeginCopyFrom(ParseState *pstate,
cstate->defexprs = defexprs;
cstate->volatile_defexprs = volatile_defexprs;
cstate->num_defaults = num_defaults;
+}
+
+/*
+ * Setup to read tuples from a file for COPY FROM.
+ *
+ * 'rel': Used as a template for the tuples
+ * 'filename': Name of server-local file to read
+ * 'attnamelist': List of char *, columns to include. NIL selects all cols.
+ * 'options': List of DefElem. See copy_opt_item in gram.y for selections.
+ *
+ * Returns a CopyState, to be passed to NextCopyFrom and related functions.
+ */
+CopyState
+BeginCopyFrom(ParseState *pstate,
+ Relation rel,
+ const char *filename,
+ bool is_program,
+ copy_data_source_cb data_source_cb,
+ List *attnamelist,
+ List *options)
+{
+ CopyState cstate;
+ bool pipe = (filename == NULL);
+ MemoryContext oldcontext;
+
+ cstate = BeginCopy(pstate, true, rel, NULL, InvalidOid, attnamelist, options);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ /* Initialize state variables */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /*
+ * Set up variables to avoid per-attribute overhead. attribute_buf is
+ * used in both text and binary modes, but we use line_buf and raw_buf
+ * only in text mode.
+ */
+ initStringInfo(&cstate->attribute_buf);
+ cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ if (!cstate->binary)
+ {
+ initStringInfo(&cstate->line_buf);
+ cstate->line_buf_converted = false;
+ }
+
+ /* Assign range table, we'll need it in CopyFrom. */
+ if (pstate)
+ cstate->range_table = pstate->p_rtable;
+
+ PopulateCstateCatalogInfo(cstate);
cstate->is_program = is_program;
if (data_source_cb)
@@ -3917,45 +3978,60 @@ CopyReadLine(CopyState cstate)
} while (CopyLoadRawBuf(cstate));
}
}
- else
- {
- /*
- * If we didn't hit EOF, then we must have transferred the EOL marker
- * to line_buf along with the data. Get rid of it.
- */
- switch (cstate->eol_type)
- {
- case EOL_NL:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CR:
- Assert(cstate->line_buf.len >= 1);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\r');
- cstate->line_buf.len--;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_CRNL:
- Assert(cstate->line_buf.len >= 2);
- Assert(cstate->line_buf.data[cstate->line_buf.len - 2] == '\r');
- Assert(cstate->line_buf.data[cstate->line_buf.len - 1] == '\n');
- cstate->line_buf.len -= 2;
- cstate->line_buf.data[cstate->line_buf.len] = '\0';
- break;
- case EOL_UNKNOWN:
- /* shouldn't get here */
- Assert(false);
- break;
- }
- }
+ ConvertToServerEncoding(cstate);
+ return result;
+}
+
+/*
+ * ClearEOLFromCopiedData - Clear EOL from the copied data.
+ */
+static void
+ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
+ int copy_line_pos, int *copy_line_size)
+{
+ /*
+ * If we didn't hit EOF, then we must have transferred the EOL marker
+ * to line_buf along with the data. Get rid of it.
+ */
+ switch (cstate->eol_type)
+ {
+ case EOL_NL:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CR:
+ Assert(*copy_line_size >= 1);
+ Assert(copy_line_data[copy_line_pos - 1] == '\r');
+ copy_line_data[copy_line_pos - 1] = '\0';
+ (*copy_line_size)--;
+ break;
+ case EOL_CRNL:
+ Assert(*copy_line_size >= 2);
+ Assert(copy_line_data[copy_line_pos - 2] == '\r');
+ Assert(copy_line_data[copy_line_pos - 1] == '\n');
+ copy_line_data[copy_line_pos - 2] = '\0';
+ *copy_line_size -= 2;
+ break;
+ case EOL_UNKNOWN:
+ /* shouldn't get here */
+ Assert(false);
+ break;
+ }
+}
+
+/*
+ * ConvertToServerEncoding - Convert contents to server encoding.
+ */
+static void
+ConvertToServerEncoding(CopyState cstate)
+{
/* Done reading the line. Convert it to server encoding. */
if (cstate->need_transcoding)
{
char *cvt;
-
cvt = pg_any_to_server(cstate->line_buf.data,
cstate->line_buf.len,
cstate->file_encoding);
@@ -3967,11 +4043,8 @@ CopyReadLine(CopyState cstate)
pfree(cvt);
}
}
-
/* Now it's safe to use the buffer in error messages */
cstate->line_buf_converted = true;
-
- return result;
}
/*
@@ -4334,6 +4407,7 @@ not_end_of_copy:
* Transfer any still-uncopied data to line_buf.
*/
REFILL_LINEBUF;
+ CLEAR_EOL_LINE();
return result;
}
--
1.8.3.1
[text/x-patch] v5-0002-Framework-for-leader-worker-in-parallel-copy.patch (31.0K, ../../CALDaNm2rRNnLoC0g9z9aDzpz1FoAanv5u00ri1d+E8Y3of5rxg@mail.gmail.com/3-v5-0002-Framework-for-leader-worker-in-parallel-copy.patch)
download | inline diff:
From 84a641370debe73a8e4f32b2628d0cb2d21ab75b Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Tue, 22 Sep 2020 13:54:45 +0530
Subject: [PATCH v5 2/6] Framework for leader/worker in parallel copy
This patch has the framework for data structures in parallel copy, leader
initialization, worker initialization, shared memory updation, starting workers,
wait for workers and workers exiting.
---
src/backend/access/transam/parallel.c | 4 +
src/backend/commands/copy.c | 742 +++++++++++++++++++++++++++++++++-
src/include/commands/copy.h | 2 +
src/tools/pgindent/typedefs.list | 7 +
4 files changed, 753 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index b042696..a3cff4b 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -25,6 +25,7 @@
#include "catalog/pg_enum.h"
#include "catalog/storage.h"
#include "commands/async.h"
+#include "commands/copy.h"
#include "executor/execParallel.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
@@ -145,6 +146,9 @@ static const struct
},
{
"parallel_vacuum_main", parallel_vacuum_main
+ },
+ {
+ "ParallelCopyMain", ParallelCopyMain
}
};
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cf7277a..cf16109 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -96,9 +96,183 @@ typedef enum CopyInsertMethod
} CopyInsertMethod;
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
+
+/*
+ * The macros DATA_BLOCK_SIZE, RINGSIZE & MAX_BLOCKS_COUNT stores the records
+ * read from the file that need to be inserted into the relation. These values
+ * help in handover of multiple records with significant size of data to be
+ * processed by each of the workers to make sure there is no context switch & the
+ * work is fairly distributed among the workers. This number showed best
+ * results in the performance tests.
+ */
+#define DATA_BLOCK_SIZE RAW_BUF_SIZE
+
+/* It can hold 1000 blocks of 64K data in DSM to be processed by the worker. */
+#define MAX_BLOCKS_COUNT 1000
+
+/*
+ * It can hold upto 10000 record information for worker to process. RINGSIZE
+ * should be a multiple of WORKER_CHUNK_COUNT, as wrap around cases is currently
+ * not handled while selecting the WORKER_CHUNK_COUNT by the worker.
+ */
+#define RINGSIZE (10 * 1000)
+
+/*
+ * Each worker will be allocated WORKER_CHUNK_COUNT of records from DSM data
+ * block to process to avoid lock contention. Read RINGSIZE comments before
+ * changing this value.
+ */
+#define WORKER_CHUNK_COUNT 50
+
+#define IsParallelCopy() (cstate->is_parallel)
+#define IsLeader() (cstate->pcdata->is_leader)
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
+ * Copy data block information.
+ * ParallelCopyDataBlock's will be created in DSM. Data read from file will be
+ * copied in these DSM data blocks. The leader process identifies the records
+ * and the record information will be shared to the workers. The workers will
+ * insert the records into the table. There can be one or more number of records
+ * in each of the data block based on the record size.
+ */
+typedef struct ParallelCopyDataBlock
+{
+ /* The number of unprocessed lines in the current block. */
+ pg_atomic_uint32 unprocessed_line_parts;
+
+ /*
+ * If the current line data is continued into another block, following_block
+ * will have the position where the remaining data need to be read.
+ */
+ uint32 following_block;
+
+ /*
+ * This flag will be set, when the leader finds out this block can be read
+ * safely by the worker. This helps the worker to start processing the line
+ * early where the line will be spread across many blocks and the worker
+ * need not wait for the complete line to be processed.
+ */
+ bool curr_blk_completed;
+ char data[DATA_BLOCK_SIZE]; /* data read from file */
+ uint8 skip_bytes;
+} ParallelCopyDataBlock;
+
+/*
+ * Individual line information.
+ * ParallelCopyLineBoundary is common data structure between leader & worker,
+ * Leader process will be populating data block, data block offset & the size of
+ * the record in DSM for the workers to copy the data into the relation.
+ * This is protected by the following sequence in the leader & worker. If they
+ * don't follow this order the worker might process wrong line_size and leader
+ * might populate the information which worker has not yet processed or in the
+ * process of processing.
+ * Leader should operate in the following order:
+ * 1) check if line_size is -1, if not wait, it means worker is still
+ * processing.
+ * 2) set line_state to LINE_LEADER_POPULATING.
+ * 3) update first_block, start_offset & cur_lineno in any order.
+ * 4) update line_size.
+ * 5) update line_state to LINE_LEADER_POPULATED.
+ * Worker should operate in the following order:
+ * 1) check line_state is LINE_LEADER_POPULATED, if not it means leader is still
+ * populating the data.
+ * 2) read line_size.
+ * 3) only one worker should choose one line for processing, this is handled by
+ * using pg_atomic_compare_exchange_u32, worker will change the state to
+ * LINE_WORKER_PROCESSING only if line_state is LINE_LEADER_POPULATED.
+ * 4) read first_block, start_offset & cur_lineno in any order.
+ * 5) process line_size data.
+ * 6) update line_size to -1.
+ */
+typedef struct ParallelCopyLineBoundary
+{
+ /* Position of the first block in data_blocks array. */
+ uint32 first_block;
+ uint32 start_offset; /* start offset of the line */
+
+ /*
+ * Size of the current line -1 means line is yet to be filled completely,
+ * 0 means empty line, >0 means line filled with line size data.
+ */
+ pg_atomic_uint32 line_size;
+ pg_atomic_uint32 line_state; /* line state */
+ uint64 cur_lineno; /* line number for error messages */
+} ParallelCopyLineBoundary;
+
+/*
+ * Circular queue used to store the line information.
+ */
+typedef struct ParallelCopyLineBoundaries
+{
+ /* Position for the leader to populate a line. */
+ uint32 pos;
+
+ /* Data read from the file/stdin by the leader process. */
+ ParallelCopyLineBoundary ring[RINGSIZE];
+} ParallelCopyLineBoundaries;
+
+/*
+ * Shared information among parallel copy workers. This will be allocated in the
+ * DSM segment.
+ */
+typedef struct ParallelCopyShmInfo
+{
+ bool is_read_in_progress; /* file read status */
+
+ /*
+ * Actual lines inserted by worker, will not be same as
+ * total_worker_processed if where condition is specified along with copy.
+ * This will be the actual records inserted into the relation.
+ */
+ pg_atomic_uint64 processed;
+
+ /*
+ * The number of records currently processed by the worker, this will also
+ * include the number of records that was filtered because of where clause.
+ */
+ pg_atomic_uint64 total_worker_processed;
+ uint64 populated; /* lines populated by leader */
+ uint32 cur_block_pos; /* current data block */
+ ParallelCopyDataBlock data_blocks[MAX_BLOCKS_COUNT]; /* data block array */
+ FullTransactionId full_transaction_id; /* xid for copy from statement */
+ CommandId mycid; /* command id */
+ ParallelCopyLineBoundaries line_boundaries; /* line array */
+} ParallelCopyShmInfo;
+
+/*
+ * Parallel copy line buffer information.
+ */
+typedef struct ParallelCopyLineBuf
+{
+ StringInfoData line_buf;
+ uint64 cur_lineno; /* line number for error messages */
+} ParallelCopyLineBuf;
+
+/*
+ * Parallel copy data information.
+ */
+typedef struct ParallelCopyData
+{
+ Oid relid; /* relation id of the table */
+ ParallelCopyShmInfo *pcshared_info; /* common info in shared memory */
+ bool is_leader;
+
+ /* line position which worker is processing */
+ uint32 worker_processed_pos;
+
+ /*
+ * Local line_buf array, workers will copy it here and release the lines
+ * for the leader to continue.
+ */
+ ParallelCopyLineBuf worker_line_buf[WORKER_CHUNK_COUNT];
+ uint32 worker_line_buf_count; /* Number of lines */
+
+ /* Current position in worker_line_buf */
+ uint32 worker_line_buf_pos;
+} ParallelCopyData;
+
+/*
* This struct contains all the state variables used throughout a COPY
* operation. For simplicity, we use the same struct for all variants of COPY,
* even though some fields are used in only some cases.
@@ -230,10 +404,38 @@ typedef struct CopyStateData
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
+ int nworkers;
+ bool is_parallel;
+ ParallelCopyData *pcdata;
/* Shorthand for number of unconsumed bytes available in raw_buf */
#define RAW_BUF_BYTES(cstate) ((cstate)->raw_buf_len - (cstate)->raw_buf_index)
} CopyStateData;
+/*
+ * This structure helps in storing the common data from CopyStateData that are
+ * required by the workers. This information will then be allocated and stored
+ * into the DSM for the worker to retrieve and copy it to CopyStateData.
+ */
+typedef struct SerializedParallelCopyState
+{
+ /* low-level state data */
+ CopyDest copy_dest; /* type of copy source/destination */
+ int file_encoding; /* file or remote side's character encoding */
+ bool need_transcoding; /* file encoding diff from server? */
+ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */
+
+ /* parameters from the COPY command */
+ bool csv_mode; /* Comma Separated Value format? */
+ bool header_line; /* CSV header line? */
+ int null_print_len; /* length of same */
+ bool force_quote_all; /* FORCE_QUOTE *? */
+ bool convert_selectively; /* do selective binary conversion? */
+
+ /* Working state for COPY FROM */
+ AttrNumber num_defaults;
+ Oid relid;
+} SerializedParallelCopyState;
+
/* DestReceiver for COPY (query) TO */
typedef struct
{
@@ -263,6 +465,22 @@ typedef struct
/* Trim the list of buffers back down to this number after flushing */
#define MAX_PARTITION_BUFFERS 32
+/* DSM keys for parallel copy. */
+#define PARALLEL_COPY_KEY_SHARED_INFO 1
+#define PARALLEL_COPY_KEY_CSTATE 2
+#define PARALLEL_COPY_KEY_NULL_PRINT 3
+#define PARALLEL_COPY_KEY_DELIM 4
+#define PARALLEL_COPY_KEY_QUOTE 5
+#define PARALLEL_COPY_KEY_ESCAPE 6
+#define PARALLEL_COPY_KEY_ATTNAME_LIST 7
+#define PARALLEL_COPY_KEY_NOT_NULL_LIST 8
+#define PARALLEL_COPY_KEY_NULL_LIST 9
+#define PARALLEL_COPY_KEY_CONVERT_LIST 10
+#define PARALLEL_COPY_KEY_WHERE_CLAUSE_STR 11
+#define PARALLEL_COPY_KEY_RANGE_TABLE 12
+#define PARALLEL_COPY_WAL_USAGE 13
+#define PARALLEL_COPY_BUFFER_USAGE 14
+
/* Stores multi-insert data related to a single relation in CopyFrom. */
typedef struct CopyMultiInsertBuffer
{
@@ -424,11 +642,478 @@ static bool CopyGetInt16(CopyState cstate, int16 *val);
static bool CopyLoadRawBuf(CopyState cstate);
static int CopyReadBinaryData(CopyState cstate, char *dest, int nbytes);
+static pg_attribute_always_inline void EndParallelCopy(ParallelContext *pcxt);
static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
List *attnamelist);
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
+
+
+/*
+ * SerializeParallelCopyState - Copy shared_cstate using cstate information.
+ */
+static pg_attribute_always_inline void
+SerializeParallelCopyState(CopyState cstate, SerializedParallelCopyState *shared_cstate)
+{
+ shared_cstate->copy_dest = cstate->copy_dest;
+ shared_cstate->file_encoding = cstate->file_encoding;
+ shared_cstate->need_transcoding = cstate->need_transcoding;
+ shared_cstate->encoding_embeds_ascii = cstate->encoding_embeds_ascii;
+ shared_cstate->csv_mode = cstate->csv_mode;
+ shared_cstate->header_line = cstate->header_line;
+ shared_cstate->null_print_len = cstate->null_print_len;
+ shared_cstate->force_quote_all = cstate->force_quote_all;
+ shared_cstate->convert_selectively = cstate->convert_selectively;
+ shared_cstate->num_defaults = cstate->num_defaults;
+ shared_cstate->relid = cstate->pcdata->relid;
+}
+
+/*
+ * RestoreString - Retrieve the string from shared memory.
+ */
+static void
+RestoreString(shm_toc *toc, int sharedkey, char **copystr)
+{
+ char *shared_str_val = (char *) shm_toc_lookup(toc, sharedkey, true);
+ if (shared_str_val)
+ *copystr = pstrdup(shared_str_val);
+}
+
+/*
+ * EstimateLineKeysStr - Estimate the size required in shared memory for the
+ * input string.
+ */
+static void
+EstimateLineKeysStr(ParallelContext *pcxt, char *inputstr)
+{
+ if (inputstr)
+ {
+ shm_toc_estimate_chunk(&pcxt->estimator, strlen(inputstr) + 1);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ }
+}
+
+/*
+ * SerializeString - Insert a string into shared memory.
+ */
+static void
+SerializeString(ParallelContext *pcxt, int key, char *inputstr)
+{
+ if (inputstr)
+ {
+ char *shmptr = (char *) shm_toc_allocate(pcxt->toc,
+ strlen(inputstr) + 1);
+ strcpy(shmptr, inputstr);
+ shm_toc_insert(pcxt->toc, key, shmptr);
+ }
+}
+
+/*
+ * PopulateParallelCopyShmInfo - Set ParallelCopyShmInfo.
+ */
+static void
+PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
+ FullTransactionId full_transaction_id)
+{
+ uint32 count;
+
+ MemSet(shared_info_ptr, 0, sizeof(ParallelCopyShmInfo));
+ shared_info_ptr->is_read_in_progress = true;
+ shared_info_ptr->cur_block_pos = -1;
+ shared_info_ptr->full_transaction_id = full_transaction_id;
+ shared_info_ptr->mycid = GetCurrentCommandId(true);
+ for (count = 0; count < RINGSIZE; count++)
+ {
+ ParallelCopyLineBoundary *lineInfo = &shared_info_ptr->line_boundaries.ring[count];
+ pg_atomic_init_u32(&(lineInfo->line_size), -1);
+ }
+}
+
+/*
+ * BeginParallelCopy - Start parallel copy tasks.
+ *
+ * Get the number of workers required to perform the parallel copy. The data
+ * structures that are required by the parallel workers will be initialized, the
+ * size required in DSM will be calculated and the necessary keys will be loaded
+ * in the DSM. The specified number of workers will then be launched.
+ */
+static ParallelContext*
+BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
+{
+ ParallelContext *pcxt;
+ ParallelCopyShmInfo *shared_info_ptr;
+ SerializedParallelCopyState *shared_cstate;
+ FullTransactionId full_transaction_id;
+ Size est_cstateshared;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ int parallel_workers = 0;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+ ParallelCopyData *pcdata;
+ MemoryContext oldcontext;
+
+ parallel_workers = Min(nworkers, max_worker_processes);
+
+ /* Can't perform copy in parallel */
+ if (parallel_workers <= 0)
+ return NULL;
+
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ MemoryContextSwitchTo(oldcontext);
+ cstate->pcdata = pcdata;
+
+ EnterParallelMode();
+ pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
+ parallel_workers);
+ Assert(pcxt->nworkers > 0);
+
+ /*
+ * Estimate size for shared information for PARALLEL_COPY_KEY_SHARED_INFO
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(ParallelCopyShmInfo));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_CSTATE */
+ est_cstateshared = MAXALIGN(sizeof(SerializedParallelCopyState));
+ shm_toc_estimate_chunk(&pcxt->estimator, est_cstateshared);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ EstimateLineKeysStr(pcxt, cstate->null_print);
+ EstimateLineKeysStr(pcxt, cstate->null_print_client);
+ EstimateLineKeysStr(pcxt, cstate->delim);
+ EstimateLineKeysStr(pcxt, cstate->quote);
+ EstimateLineKeysStr(pcxt, cstate->escape);
+
+ if (cstate->whereClause != NULL)
+ {
+ whereClauseStr = nodeToString(cstate->whereClause);
+ EstimateLineKeysStr(pcxt, whereClauseStr);
+ }
+
+ if (cstate->range_table != NULL)
+ {
+ rangeTableStr = nodeToString(cstate->range_table);
+ EstimateLineKeysStr(pcxt, rangeTableStr);
+ }
+
+ /* Estimate the size for shared information for PARALLEL_COPY_KEY_XID. */
+ shm_toc_estimate_chunk(&pcxt->estimator, sizeof(FullTransactionId));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_ATTNAME_LIST.
+ */
+ if (attnamelist != NIL)
+ {
+ attnameListStr = nodeToString(attnamelist);
+ EstimateLineKeysStr(pcxt, attnameListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NOT_NULL_LIST.
+ */
+ if (cstate->force_notnull != NIL)
+ {
+ notnullListStr = nodeToString(cstate->force_notnull);
+ EstimateLineKeysStr(pcxt, notnullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_NULL_LIST.
+ */
+ if (cstate->force_null != NIL)
+ {
+ nullListStr = nodeToString(cstate->force_null);
+ EstimateLineKeysStr(pcxt, nullListStr);
+ }
+
+ /*
+ * Estimate the size for shared information for
+ * PARALLEL_COPY_KEY_CONVERT_LIST.
+ */
+ if (cstate->convert_select != NIL)
+ {
+ convertListStr = nodeToString(cstate->convert_select);
+ EstimateLineKeysStr(pcxt, convertListStr);
+ }
+
+ /*
+ * Estimate space for WalUsage and BufferUsage -- PARALLEL_COPY_WAL_USAGE
+ * and PARALLEL_COPY_BUFFER_USAGE.
+ */
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+ shm_toc_estimate_chunk(&pcxt->estimator,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+ InitializeParallelDSM(pcxt);
+
+ /* If no DSM segment was available, back out (do serial copy) */
+ if (pcxt->seg == NULL)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /* Allocate shared memory for PARALLEL_COPY_KEY_SHARED_INFO */
+ shared_info_ptr = (ParallelCopyShmInfo *) shm_toc_allocate(pcxt->toc, sizeof(ParallelCopyShmInfo));
+ PopulateParallelCopyShmInfo(shared_info_ptr, full_transaction_id);
+
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_SHARED_INFO, shared_info_ptr);
+ pcdata->pcshared_info = shared_info_ptr;
+ pcdata->relid = relid;
+
+ /* Store shared build state, for which we reserved space. */
+ shared_cstate = (SerializedParallelCopyState *) shm_toc_allocate(pcxt->toc, est_cstateshared);
+
+ /* copy cstate variables. */
+ SerializeParallelCopyState(cstate, shared_cstate);
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_KEY_CSTATE, shared_cstate);
+
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_ATTNAME_LIST, attnameListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NOT_NULL_LIST, notnullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_LIST, nullListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_CONVERT_LIST, convertListStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, whereClauseStr);
+ SerializeString(pcxt, PARALLEL_COPY_KEY_RANGE_TABLE, rangeTableStr);
+
+ /*
+ * Allocate space for each worker's WalUsage and BufferUsage; no need to
+ * initialize.
+ */
+ walusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(WalUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_WAL_USAGE, walusage);
+ bufferusage = shm_toc_allocate(pcxt->toc,
+ mul_size(sizeof(BufferUsage), pcxt->nworkers));
+ shm_toc_insert(pcxt->toc, PARALLEL_COPY_BUFFER_USAGE, bufferusage);
+
+ LaunchParallelWorkers(pcxt);
+ if (pcxt->nworkers_launched == 0)
+ {
+ EndParallelCopy(pcxt);
+ return NULL;
+ }
+
+ /*
+ * Caller needs to wait for all launched workers when we return. Make sure
+ * that the failure-to-start case will not hang forever.
+ */
+ WaitForParallelWorkersToAttach(pcxt);
+
+ pcdata->is_leader = true;
+ cstate->is_parallel = true;
+ return pcxt;
+}
+
+/*
+ * EndParallelCopy - End the parallel copy tasks.
+ */
+static pg_attribute_always_inline void
+EndParallelCopy(ParallelContext *pcxt)
+{
+ Assert(!IsParallelWorker());
+
+ DestroyParallelContext(pcxt);
+ ExitParallelMode();
+}
+
+/*
+ * InitializeParallelCopyInfo - Initialize parallel worker.
+ */
+static void
+InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
+ CopyState cstate, List *attnamelist)
+{
+ uint32 count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ cstate->copy_dest = shared_cstate->copy_dest;
+ cstate->file_encoding = shared_cstate->file_encoding;
+ cstate->need_transcoding = shared_cstate->need_transcoding;
+ cstate->encoding_embeds_ascii = shared_cstate->encoding_embeds_ascii;
+ cstate->csv_mode = shared_cstate->csv_mode;
+ cstate->header_line = shared_cstate->header_line;
+ cstate->null_print_len = shared_cstate->null_print_len;
+ cstate->force_quote_all = shared_cstate->force_quote_all;
+ cstate->convert_selectively = shared_cstate->convert_selectively;
+ cstate->num_defaults = shared_cstate->num_defaults;
+ pcdata->relid = shared_cstate->relid;
+
+ PopulateCommonCstateInfo(cstate, tup_desc, attnamelist);
+
+ /* Initialize state variables. */
+ cstate->reached_eof = false;
+ cstate->eol_type = EOL_UNKNOWN;
+ cstate->cur_relname = RelationGetRelationName(cstate->rel);
+ cstate->cur_lineno = 0;
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+
+ /* Set up variables to avoid per-attribute overhead. */
+ initStringInfo(&cstate->attribute_buf);
+
+ initStringInfo(&cstate->line_buf);
+ for (count = 0; count < WORKER_CHUNK_COUNT; count++)
+ initStringInfo(&pcdata->worker_line_buf[count].line_buf);
+
+ cstate->line_buf_converted = false;
+ cstate->raw_buf = NULL;
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+}
+
+/*
+ * ParallelCopyMain - Parallel copy worker's code.
+ *
+ * Where clause handling, convert tuple to columns, add default null values for
+ * the missing columns that are not present in that record. Find the partition
+ * if it is partitioned table, invoke before row insert Triggers, handle
+ * constraints and insert the tuples.
+ */
+void
+ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
+{
+ CopyState cstate;
+ ParallelCopyData *pcdata;
+ ParallelCopyShmInfo *pcshared_info;
+ SerializedParallelCopyState *shared_cstate;
+ Relation rel = NULL;
+ MemoryContext oldcontext;
+ List *attlist = NIL;
+ char *whereClauseStr = NULL;
+ char *rangeTableStr = NULL;
+ char *attnameListStr = NULL;
+ char *notnullListStr = NULL;
+ char *nullListStr = NULL;
+ char *convertListStr = NULL;
+ WalUsage *walusage;
+ BufferUsage *bufferusage;
+
+ /* Allocate workspace and zero all fields. */
+ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData));
+
+ /*
+ * We allocate everything used by a cstate in a new memory context. This
+ * avoids memory leaks during repeated use of COPY in a query.
+ */
+ cstate->copycontext = AllocSetContextCreate(CurrentMemoryContext,
+ "COPY",
+ ALLOCSET_DEFAULT_SIZES);
+ oldcontext = MemoryContextSwitchTo(cstate->copycontext);
+
+ pcdata = (ParallelCopyData *) palloc0(sizeof(ParallelCopyData));
+ cstate->pcdata = pcdata;
+ pcdata->is_leader = false;
+ pcdata->worker_processed_pos = -1;
+ cstate->is_parallel = true;
+ pcshared_info = (ParallelCopyShmInfo *) shm_toc_lookup(toc, PARALLEL_COPY_KEY_SHARED_INFO, false);
+
+ ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
+
+ pcdata->pcshared_info = pcshared_info;
+
+ shared_cstate = (SerializedParallelCopyState *) shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
+ cstate->null_print = (char *) shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_DELIM, &cstate->delim);
+ RestoreString(toc, PARALLEL_COPY_KEY_QUOTE, &cstate->quote);
+ RestoreString(toc, PARALLEL_COPY_KEY_ESCAPE, &cstate->escape);
+ RestoreString(toc, PARALLEL_COPY_KEY_ATTNAME_LIST, &attnameListStr);
+ if (attnameListStr)
+ attlist = (List *) stringToNode(attnameListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NOT_NULL_LIST, ¬nullListStr);
+ if (notnullListStr)
+ cstate->force_notnull = (List *) stringToNode(notnullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_NULL_LIST, &nullListStr);
+ if (nullListStr)
+ cstate->force_null = (List *) stringToNode(nullListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_CONVERT_LIST, &convertListStr);
+ if (convertListStr)
+ cstate->convert_select = (List *) stringToNode(convertListStr);
+
+ RestoreString(toc, PARALLEL_COPY_KEY_WHERE_CLAUSE_STR, &whereClauseStr);
+ RestoreString(toc, PARALLEL_COPY_KEY_RANGE_TABLE, &rangeTableStr);
+
+ if (whereClauseStr)
+ {
+ Node *whereClauseCnvrtdFrmStr = (Node *) stringToNode(whereClauseStr);
+ cstate->whereClause = whereClauseCnvrtdFrmStr;
+ }
+
+ if (rangeTableStr)
+ {
+ List *rangeTableCnvrtdFrmStr = (List *) stringToNode(rangeTableStr);
+ cstate->range_table = rangeTableCnvrtdFrmStr;
+ }
+
+ /* Open and lock the relation, using the appropriate lock type. */
+ rel = table_open(shared_cstate->relid, RowExclusiveLock);
+ cstate->rel = rel;
+ InitializeParallelCopyInfo(shared_cstate, cstate, attlist);
+
+ CopyFrom(cstate);
+
+ if (rel != NULL)
+ table_close(rel, RowExclusiveLock);
+
+ /* Report WAL/buffer usage during parallel execution */
+ bufferusage = shm_toc_lookup(toc, PARALLEL_COPY_BUFFER_USAGE, false);
+ walusage = shm_toc_lookup(toc, PARALLEL_COPY_WAL_USAGE, false);
+ InstrEndParallelQuery(&bufferusage[ParallelWorkerNumber],
+ &walusage[ParallelWorkerNumber]);
+
+ MemoryContextSwitchTo(oldcontext);
+ pfree(cstate);
+ return;
+}
+
+/*
+ * ParallelCopyFrom - parallel copy leader's functionality.
+ *
+ * Leader executes the before statement for before statement trigger, if before
+ * statement trigger is present. It will read the table data from the file and
+ * copy the contents to DSM data blocks. It will then read the input contents
+ * from the DSM data block and identify the records based on line breaks. This
+ * information is called line or a record that need to be inserted into a
+ * relation. The line information will be stored in ParallelCopyLineBoundary DSM
+ * data structure. Workers will then process this information and insert the
+ * data in to table. It will repeat this process until the all data is read from
+ * the file and all the DSM data blocks are processed. While processing if
+ * leader identifies that DSM Data blocks or DSM ParallelCopyLineBoundary data
+ * structures is full, leader will wait till the worker frees up some entries
+ * and repeat the process. It will wait till all the lines populated are
+ * processed by the workers and exits.
+ */
+static void
+ParallelCopyFrom(CopyState cstate)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+
+ pcshared_info->is_read_in_progress = false;
+ cstate->cur_lineno = 0;
+}
+
/*
* Send copy start/stop messages for frontend copies. These have changed
* in past protocol redesigns.
@@ -1141,6 +1826,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
if (is_from)
{
+ ParallelContext *pcxt = NULL;
Assert(rel);
/* check read-only transaction and parallel mode */
@@ -1150,7 +1836,24 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);
cstate->whereClause = whereClause;
- *processed = CopyFrom(cstate); /* copy from file to database */
+ cstate->is_parallel = false;
+
+ if (cstate->nworkers > 0)
+ pcxt = BeginParallelCopy(cstate->nworkers, cstate, stmt->attlist,
+ relid);
+
+ if (pcxt)
+ {
+ ParallelCopyFrom(cstate);
+
+ /* Wait for all copy workers to finish */
+ WaitForParallelWorkersToFinish(pcxt);
+ *processed = pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+ EndParallelCopy(pcxt);
+ }
+ else
+ *processed = CopyFrom(cstate); /* copy from file to database */
+
EndCopyFrom(cstate);
}
else
@@ -1199,6 +1902,7 @@ ProcessCopyOptions(ParseState *pstate,
cstate->is_copy_from = is_from;
cstate->file_encoding = -1;
+ cstate->nworkers = -1;
/* Extract options from the statement node tree */
foreach(option, options)
@@ -1367,6 +2071,39 @@ ProcessCopyOptions(ParseState *pstate,
defel->defname),
parser_errposition(pstate, defel->location)));
}
+ else if (strcmp(defel->defname, "parallel") == 0)
+ {
+ int val;
+ bool parsed;
+ char *strval;
+
+ if (!cstate->is_copy_from)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("parallel option is supported only for copy from"),
+ parser_errposition(pstate, defel->location)));
+ if (cstate->nworkers >= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options"),
+ parser_errposition(pstate, defel->location)));
+
+ strval = defGetString(defel);
+ parsed = parse_int(strval, &val, 0, NULL);
+ if (!parsed)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for integer option \"%s\": %s",
+ defel->defname, strval)));
+ if (val < 1 || val > 1024)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value %s out of bounds for option \"%s\"",
+ strval, defel->defname),
+ errdetail("Valid values are between \"%d\" and \"%d\".",
+ 1, 1024)));
+ cstate->nworkers = val;
+ }
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1720,7 +2457,8 @@ BeginCopy(ParseState *pstate,
/*
* PopulateCommonCstateInfo - Populates the common variables required for copy
- * from operation. This is a helper function for BeginCopy function.
+ * from operation. This is a helper function for BeginCopy &
+ * InitializeParallelCopyInfo function.
*/
static void
PopulateCommonCstateInfo(CopyState cstate, TupleDesc tupDesc, List *attnamelist)
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index c639833..82843c6 100644
--- a/src/include/commands/copy.h
+++ b/src/include/commands/copy.h
@@ -14,6 +14,7 @@
#ifndef COPY_H
#define COPY_H
+#include "access/parallel.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
#include "parser/parse_node.h"
@@ -41,4 +42,5 @@ extern uint64 CopyFrom(CopyState cstate);
extern DestReceiver *CreateCopyDestReceiver(void);
+extern void ParallelCopyMain(dsm_segment *seg, shm_toc *toc);
#endif /* COPY_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b1afb34..509c695 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1702,6 +1702,12 @@ ParallelBitmapHeapState
ParallelBlockTableScanDesc
ParallelCompletionPtr
ParallelContext
+ParallelCopyLineBoundaries
+ParallelCopyLineBoundary
+ParallelCopyData
+ParallelCopyDataBlock
+ParallelCopyLineBuf
+ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
ParallelHashJoinBatch
@@ -2219,6 +2225,7 @@ SerCommitSeqNo
SerialControl
SerializableXactHandle
SerializedActiveRelMaps
+SerializedParallelCopyState
SerializedReindexState
SerializedSnapshotData
SerializedTransactionState
--
1.8.3.1
[text/x-patch] v5-0003-Allow-copy-from-command-to-process-data-from-file.patch (43.0K, ../../CALDaNm2rRNnLoC0g9z9aDzpz1FoAanv5u00ri1d+E8Y3of5rxg@mail.gmail.com/4-v5-0003-Allow-copy-from-command-to-process-data-from-file.patch)
download | inline diff:
From b7d2f516f9ae9088663d7e68247541ce6b019c40 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Tue, 22 Sep 2020 13:00:32 +0530
Subject: [PATCH v5 3/6] Allow copy from command to process data from
file/STDIN contents to a table in parallel.
This feature allows the copy from to leverage multiple CPUs in order to copy
data from file/STDIN to a table. This adds a PARALLEL option to COPY FROM
command where the user can specify the number of workers that can be used
to perform the COPY FROM command.
The backend, to which the "COPY FROM" query is submitted acts as leader with
the responsibility of reading data from the file/stdin, launching at most n
number of workers as specified with PARALLEL 'n' option in the "COPY FROM"
query. The leader populates the common data required for the workers execution
in the DSM and shares it with the workers. The leader then executes before
statement triggers if there exists any. Leader populates DSM lines which
includes the start offset and line size, while populating the lines it reads
as many blocks as required into the DSM data blocks from the file. Each block
is of 64K size. The leader parses the data to identify a line, the existing
logic from CopyReadLineText which identifies the lines with some changes was
used for this. Leader checks if a free line is available to copy the
information, if there is no free line it waits till the required line is
freed up by the worker and then copies the identified lines information
(offset & line size) into the DSM lines. This process is repeated till the
complete file is processed. Simultaneously, the workers cache the lines(50)
locally into the local memory and release the lines to the leader for further
populating. Each worker processes the lines that it cached and inserts it into
the table. The leader does not participate in the insertion of data, leaders
only responsibility will be to identify the lines as fast as possible for the
workers to do the actual copy operation. The leader waits till all the lines
populated are processed by the workers and exits.
We have chosen this design based on the reason "that everything stalls if the
leader doesn't accept further input data, as well as when there are no
available splitted chunks so it doesn't seem like a good idea to have the
leader do other work. This is backed by the performance data where we have
seen that with 1 worker there is just a 5-10% performance difference".
---
src/backend/access/common/toast_internals.c | 11 +-
src/backend/access/heap/heapam.c | 11 -
src/backend/access/transam/xact.c | 31 +
src/backend/commands/copy.c | 896 ++++++++++++++++++++++++++--
src/bin/psql/tab-complete.c | 2 +-
src/include/access/xact.h | 2 +
src/tools/pgindent/typedefs.list | 1 +
7 files changed, 902 insertions(+), 52 deletions(-)
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 25a81e5..586d53d 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -17,6 +17,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heaptoast.h"
+#include "access/parallel.h"
#include "access/table.h"
#include "access/toast_internals.h"
#include "access/xact.h"
@@ -116,7 +117,15 @@ toast_save_datum(Relation rel, Datum value,
TupleDesc toasttupDesc;
Datum t_values[3];
bool t_isnull[3];
- CommandId mycid = GetCurrentCommandId(true);
+
+ /*
+ * Parallel copy can insert toast tuples, in case of parallel copy the
+ * command would have been set already by calling AssignCommandIdForWorker.
+ * For parallel copy call GetCurrentCommandId to get currentCommandId by
+ * passing used as false, as this is taken care earlier.
+ */
+ CommandId mycid = IsParallelWorker() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
struct varlena *result;
struct varatt_external toast_pointer;
union
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 1585861..1602525 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2043,17 +2043,6 @@ static HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
- /*
- * To allow parallel inserts, we need to ensure that they are safe to be
- * performed in workers. We have the infrastructure to allow parallel
- * inserts in general except for the cases where inserts generate a new
- * CommandId (eg. inserts into a table having a foreign key column).
- */
- if (IsParallelWorker())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot insert tuples in a parallel worker")));
-
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index af6afce..e983f78 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -517,6 +517,37 @@ GetCurrentFullTransactionIdIfAny(void)
}
/*
+ * AssignFullTransactionIdForWorker
+ *
+ * For parallel copy, transaction id of leader will be used by the workers.
+ */
+void
+AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId)
+{
+ TransactionState s = CurrentTransactionState;
+
+ Assert((IsInParallelMode() || IsParallelWorker()));
+ s->fullTransactionId = fullTransactionId;
+}
+
+/*
+ * AssignCommandIdForWorker
+ *
+ * For parallel copy, command id of leader will be used by the workers.
+ */
+void
+AssignCommandIdForWorker(CommandId commandId, bool used)
+{
+ Assert((IsInParallelMode() || IsParallelWorker()));
+
+ /* this is global to a transaction, not subtransaction-local */
+ if (used)
+ currentCommandIdUsed = true;
+
+ currentCommandId = commandId;
+}
+
+/*
* MarkCurrentTransactionIdLoggedIfAny
*
* Remember that the current xid - if it is assigned - now has been wal logged.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cf16109..ba188d7 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -26,6 +26,7 @@
#include "access/xlog.h"
#include "catalog/dependency.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_proc_d.h"
#include "catalog/pg_type.h"
#include "commands/copy.h"
#include "commands/defrem.h"
@@ -40,11 +41,13 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
+#include "pgstat.h"
#include "port/pg_bswap.h"
#include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
@@ -95,6 +98,18 @@ typedef enum CopyInsertMethod
CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */
} CopyInsertMethod;
+/*
+ * State of the line.
+ */
+typedef enum ParallelCopyLineState
+{
+ LINE_INIT, /* initial state of line */
+ LINE_LEADER_POPULATING, /* leader processing line */
+ LINE_LEADER_POPULATED, /* leader completed populating line */
+ LINE_WORKER_PROCESSING, /* worker processing line */
+ LINE_WORKER_PROCESSED /* worker completed processing line */
+} ParallelCopyLineState;
+
#define RAW_BUF_SIZE 65536 /* we palloc RAW_BUF_SIZE+1 bytes */
/*
@@ -126,6 +141,7 @@ typedef enum CopyInsertMethod
#define IsParallelCopy() (cstate->is_parallel)
#define IsLeader() (cstate->pcdata->is_leader)
+#define IsWorker() (IsParallelCopy() && !IsLeader())
#define IsHeaderLine() (cstate->header_line && cstate->cur_lineno == 1)
/*
@@ -559,9 +575,13 @@ if (1) \
{ \
if (raw_buf_ptr > cstate->raw_buf_index) \
{ \
- appendBinaryStringInfo(&cstate->line_buf, \
- cstate->raw_buf + cstate->raw_buf_index, \
- raw_buf_ptr - cstate->raw_buf_index); \
+ if (!IsParallelCopy()) \
+ appendBinaryStringInfo(&cstate->line_buf, \
+ cstate->raw_buf + cstate->raw_buf_index, \
+ raw_buf_ptr - cstate->raw_buf_index); \
+ else \
+ line_size += raw_buf_ptr - cstate->raw_buf_index; \
+ \
cstate->raw_buf_index = raw_buf_ptr; \
} \
} else ((void) 0)
@@ -574,26 +594,65 @@ if (1) \
goto not_end_of_copy; \
} else ((void) 0)
+/* Begin parallel copy Macros */
+#define SET_NEWLINE_SIZE() \
+{ \
+ if (cstate->eol_type == EOL_NL || cstate->eol_type == EOL_CR) \
+ new_line_size = 1; \
+ else if (cstate->eol_type == EOL_CRNL) \
+ new_line_size = 2; \
+ else \
+ new_line_size = 0; \
+}
+
+/*
+ * COPY_WAIT_TO_PROCESS - Wait before continuing to process.
+ */
+#define COPY_WAIT_TO_PROCESS() \
+{ \
+ CHECK_FOR_INTERRUPTS(); \
+ (void) WaitLatch(MyLatch, \
+ WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, \
+ 1L, WAIT_EVENT_PG_SLEEP); \
+ ResetLatch(MyLatch); \
+}
+
/*
* CLEAR_EOL_LINE - Wrapper for clearing EOL.
*/
#define CLEAR_EOL_LINE() \
if (!result && !IsHeaderLine()) \
- ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
- cstate->line_buf.len, \
- &cstate->line_buf.len) \
+{ \
+ if (IsParallelCopy()) \
+ ClearEOLFromCopiedData(cstate, cstate->raw_buf, \
+ raw_buf_ptr, &line_size); \
+ else \
+ ClearEOLFromCopiedData(cstate, cstate->line_buf.data, \
+ cstate->line_buf.len, \
+ &cstate->line_buf.len); \
+} \
/*
* INCREMENTPROCESSED - Increment the lines processed.
*/
-#define INCREMENTPROCESSED(processed) \
-processed++;
+#define INCREMENTPROCESSED(processed) \
+{ \
+ if (!IsParallelCopy()) \
+ processed++; \
+ else \
+ pg_atomic_add_fetch_u64(&cstate->pcdata->pcshared_info->processed, 1); \
+}
/*
* RETURNPROCESSED - Get the lines processed.
*/
#define RETURNPROCESSED(processed) \
-return processed;
+if (!IsParallelCopy()) \
+ return processed; \
+else \
+ return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+
+/* End parallel copy Macros */
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
@@ -648,7 +707,10 @@ static void PopulateCommonCstateInfo(CopyState cstate, TupleDesc tup_desc,
static void ClearEOLFromCopiedData(CopyState cstate, char *copy_line_data,
int copy_line_pos, int *copy_line_size);
static void ConvertToServerEncoding(CopyState cstate);
-
+static void ExecBeforeStmtTrigger(CopyState cstate);
+static void CheckTargetRelValidity(CopyState cstate);
+static void PopulateCstateCatalogInfo(CopyState cstate);
+static pg_attribute_always_inline uint32 GetLinePosition(CopyState cstate);
/*
* SerializeParallelCopyState - Copy shared_cstate using cstate information.
@@ -731,6 +793,137 @@ PopulateParallelCopyShmInfo(ParallelCopyShmInfo *shared_info_ptr,
}
/*
+ * IsTriggerFunctionParallelSafe - Check if the trigger function is parallel
+ * safe for the triggers. Return false if any one of the trigger has parallel
+ * unsafe function.
+ */
+static pg_attribute_always_inline bool
+IsTriggerFunctionParallelSafe(TriggerDesc *trigdesc)
+{
+ int i;
+ for (i = 0; i < trigdesc->numtriggers; i++)
+ {
+ Trigger *trigger = &trigdesc->triggers[i];
+ int trigtype = RI_TRIGGER_NONE;
+
+ if (func_parallel(trigger->tgfoid) != PROPARALLEL_SAFE)
+ return false;
+
+ /* If the trigger is parallel safe, also look for RI_TRIGGER. */
+ trigtype = RI_FKey_trigger_type(trigger->tgfoid);
+ if (trigtype == RI_TRIGGER_PK || trigtype == RI_TRIGGER_FK)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * CheckExprParallelSafety - Determine parallel safety of volatile expressions
+ * in default clause of column definition or in where clause and return true if
+ * they are parallel safe.
+ */
+static pg_attribute_always_inline bool
+CheckExprParallelSafety(CopyState cstate)
+{
+ if (contain_volatile_functions(cstate->whereClause))
+ {
+ if (max_parallel_hazard((Query *) cstate->whereClause) != PROPARALLEL_SAFE)
+ return false;
+ }
+
+ /*
+ * Check if any of the column has volatile default expression. if yes, and
+ * they are not parallel safe, then parallelism is not allowed. For
+ * instance, if there are any serial/bigserial columns for which nextval()
+ * default expression which is parallel unsafe is associated, parallelism
+ * should not be allowed. In non parallel copy volatile functions are not
+ * checked for nextval().
+ */
+ if (cstate->defexprs != NULL && cstate->num_defaults != 0)
+ {
+ int i;
+ for (i = 0; i < cstate->num_defaults; i++)
+ {
+ bool volatile_expr = contain_volatile_functions((Node *)cstate->defexprs[i]->expr);
+ if (volatile_expr &&
+ (max_parallel_hazard((Query *)cstate->defexprs[i]->expr)) !=
+ PROPARALLEL_SAFE)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * FindInsertMethod - Determine insert mode single, multi, or multi conditional.
+ */
+static pg_attribute_always_inline CopyInsertMethod
+FindInsertMethod(CopyState cstate)
+{
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_before_row ||
+ cstate->rel->trigdesc->trig_insert_instead_row))
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+ cstate->rel->trigdesc != NULL &&
+ cstate->rel->trigdesc->trig_insert_new_table)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ return CIM_SINGLE;
+
+ if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ return CIM_MULTI_CONDITIONAL;
+
+ return CIM_MULTI;
+}
+
+/*
+ * IsParallelCopyAllowed - Check for the cases where parallel copy is not
+ * applicable.
+ */
+static pg_attribute_always_inline bool
+IsParallelCopyAllowed(CopyState cstate)
+{
+ /* Parallel copy not allowed for frontend (2.0 protocol) & binary option. */
+ if ((cstate->copy_dest == COPY_OLD_FE) || cstate->binary)
+ return false;
+
+ /* Check if copy is into foreign table or temporary table. */
+ if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
+ RelationUsesLocalBuffers(cstate->rel))
+ return false;
+
+ /* Check if trigger function is parallel safe. */
+ if (cstate->rel->trigdesc != NULL &&
+ !IsTriggerFunctionParallelSafe(cstate->rel->trigdesc))
+ return false;
+
+ /*
+ * Check if there is after statement or instead of trigger or transition
+ * table triggers.
+ */
+ if (cstate->rel->trigdesc != NULL &&
+ (cstate->rel->trigdesc->trig_insert_after_statement ||
+ cstate->rel->trigdesc->trig_insert_instead_row ||
+ cstate->rel->trigdesc->trig_insert_new_table))
+ return false;
+
+ /* Check if the volatile expressions are parallel safe, if present any. */
+ if (!CheckExprParallelSafety(cstate))
+ return false;
+
+ /* Check if the insertion mode is single. */
+ if (FindInsertMethod(cstate) == CIM_SINGLE)
+ return false;
+
+ return true;
+}
+
+/*
* BeginParallelCopy - Start parallel copy tasks.
*
* Get the number of workers required to perform the parallel copy. The data
@@ -758,6 +951,7 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
ParallelCopyData *pcdata;
MemoryContext oldcontext;
+ CheckTargetRelValidity(cstate);
parallel_workers = Min(nworkers, max_worker_processes);
/* Can't perform copy in parallel */
@@ -769,6 +963,15 @@ BeginParallelCopy(int nworkers, CopyState cstate, List *attnamelist, Oid relid)
MemoryContextSwitchTo(oldcontext);
cstate->pcdata = pcdata;
+ /*
+ * User chosen parallel copy. Determine if the parallel copy is actually
+ * allowed. If not, go with the non-parallel mode.
+ */
+ if (!IsParallelCopyAllowed(cstate))
+ return NULL;
+
+ full_transaction_id = GetCurrentFullTransactionId();
+
EnterParallelMode();
pcxt = CreateParallelContext("postgres", "ParallelCopyMain",
parallel_workers);
@@ -977,9 +1180,214 @@ InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
cstate->line_buf_converted = false;
cstate->raw_buf = NULL;
cstate->raw_buf_index = cstate->raw_buf_len = 0;
+
+ PopulateCstateCatalogInfo(cstate);
+
+ /* Create workspace for CopyReadAttributes results. */
+ if (!cstate->binary)
+ {
+ AttrNumber attr_count = list_length(cstate->attnumlist);
+
+ cstate->max_fields = attr_count;
+ cstate->raw_fields = (char **) palloc(attr_count * sizeof(char *));
+ }
+}
+
+/*
+ * CacheLineInfo - Cache the line information to local memory.
+ */
+static bool
+CacheLineInfo(CopyState cstate, uint32 buff_count)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyData *pcdata = cstate->pcdata;
+ uint32 write_pos;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 offset;
+ int dataSize;
+ int copiedSize = 0;
+
+ resetStringInfo(&pcdata->worker_line_buf[buff_count].line_buf);
+ write_pos = GetLinePosition(cstate);
+ if (-1 == write_pos)
+ return true;
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ if (pg_atomic_read_u32(&lineInfo->line_size) == 0)
+ goto empty_data_line_update;
+
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ /* Get the offset information from where the data must be copied. */
+ offset = lineInfo->start_offset;
+ pcdata->worker_line_buf[buff_count].cur_lineno = lineInfo->cur_lineno;
+
+ elog(DEBUG1, "[Worker] Processing - line position:%d, block:%d, unprocessed lines:%d, offset:%d, line size:%d",
+ write_pos, lineInfo->first_block,
+ pg_atomic_read_u32(&data_blk_ptr->unprocessed_line_parts),
+ offset, pg_atomic_read_u32(&lineInfo->line_size));
+
+ for (;;)
+ {
+ uint8 skip_bytes = data_blk_ptr->skip_bytes;
+
+ /*
+ * There is a possibility that the loop embedded at the bottom of the
+ * current loop has come out because data_blk_ptr->curr_blk_completed
+ * is set, but dataSize read might be an old value, if
+ * data_blk_ptr->curr_blk_completed and the line is completed, line_size
+ * will be set. Read the line_size again to be sure if it is completed
+ * or partial block.
+ */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+ if (dataSize)
+ {
+ int remainingSize = dataSize - copiedSize;
+ if (!remainingSize)
+ break;
+
+ /* Whole line is in current block. */
+ if (remainingSize + offset + skip_bytes < DATA_BLOCK_SIZE)
+ {
+ appendBinaryStringInfo(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ remainingSize);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts,
+ 1);
+ break;
+ }
+ else
+ {
+ /* Line is spread across the blocks. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+ while (copiedSize < dataSize)
+ {
+ uint32 currentBlockCopySize;
+ ParallelCopyDataBlock *currBlkPtr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ skip_bytes = currBlkPtr->skip_bytes;
+
+ /*
+ * If complete data is present in current block use
+ * dataSize - copiedSize, or copy the whole block from
+ * current block.
+ */
+ currentBlockCopySize = Min(dataSize - copiedSize, DATA_BLOCK_SIZE - skip_bytes);
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &currBlkPtr->data[0],
+ currentBlockCopySize);
+ pg_atomic_sub_fetch_u32(&currBlkPtr->unprocessed_line_parts, 1);
+ copiedSize += currentBlockCopySize;
+ data_blk_ptr = currBlkPtr;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ /* Copy this complete block from the current offset. */
+ uint32 lineInCurrentBlock = (DATA_BLOCK_SIZE - skip_bytes) - offset;
+ appendBinaryStringInfoNT(&pcdata->worker_line_buf[buff_count].line_buf,
+ &data_blk_ptr->data[offset],
+ lineInCurrentBlock);
+ pg_atomic_sub_fetch_u32(&data_blk_ptr->unprocessed_line_parts, 1);
+ copiedSize += lineInCurrentBlock;
+
+ /*
+ * Reset the offset. For the first copy, copy from the offset. For
+ * the subsequent copy the complete block.
+ */
+ offset = 0;
+
+ /* Set data_blk_ptr to the following block. */
+ data_blk_ptr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
+ }
+
+ for (;;)
+ {
+ /* Get the size of this line */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ /*
+ * If the data is present in current block lineInfo->line_size
+ * will be updated. If the data is spread across the blocks either
+ * of lineInfo->line_size or data_blk_ptr->curr_blk_completed can
+ * be updated. lineInfo->line_size will be updated if the complete
+ * read is finished. data_blk_ptr->curr_blk_completed will be
+ * updated if processing of current block is finished and data
+ * processing is not finished.
+ */
+ if (data_blk_ptr->curr_blk_completed || (dataSize != -1))
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+ }
+
+empty_data_line_update:
+ elog(DEBUG1, "[Worker] Completed processing line:%d", write_pos);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_WORKER_PROCESSED);
+ pg_atomic_write_u32(&lineInfo->line_size, -1);
+ pg_atomic_add_fetch_u64(&pcshared_info->total_worker_processed, 1);
+ return false;
}
/*
+ * GetWorkerLine - Returns a line for worker to process.
+ */
+static bool
+GetWorkerLine(CopyState cstate)
+{
+ uint32 buff_count;
+ ParallelCopyData *pcdata = cstate->pcdata;
+
+ /*
+ * Copy the line data to line_buf and release the line position so that the
+ * worker can continue loading data.
+ */
+ if (pcdata->worker_line_buf_pos < pcdata->worker_line_buf_count)
+ goto return_line;
+
+ pcdata->worker_line_buf_pos = 0;
+ pcdata->worker_line_buf_count = 0;
+
+ for (buff_count = 0; buff_count < WORKER_CHUNK_COUNT; buff_count++)
+ {
+ bool result = CacheLineInfo(cstate, buff_count);
+ if (result)
+ break;
+
+ pcdata->worker_line_buf_count++;
+ }
+
+ if (pcdata->worker_line_buf_count)
+ goto return_line;
+ else
+ resetStringInfo(&cstate->line_buf);
+
+ return true;
+
+return_line:
+ cstate->line_buf = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].line_buf;
+ cstate->cur_lineno = pcdata->worker_line_buf[pcdata->worker_line_buf_pos].cur_lineno;
+ cstate->line_buf_valid = true;
+
+ /* Mark that encoding conversion hasn't occurred yet. */
+ cstate->line_buf_converted = false;
+ ConvertToServerEncoding(cstate);
+ pcdata->worker_line_buf_pos++;
+ return false;
+ }
+
+/*
* ParallelCopyMain - Parallel copy worker's code.
*
* Where clause handling, convert tuple to columns, add default null values for
@@ -1028,6 +1436,8 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
ereport(DEBUG1, (errmsg("Starting parallel copy worker")));
pcdata->pcshared_info = pcshared_info;
+ AssignFullTransactionIdForWorker(pcshared_info->full_transaction_id);
+ AssignCommandIdForWorker(pcshared_info->mycid, true);
shared_cstate = (SerializedParallelCopyState *) shm_toc_lookup(toc, PARALLEL_COPY_KEY_CSTATE, false);
cstate->null_print = (char *) shm_toc_lookup(toc, PARALLEL_COPY_KEY_NULL_PRINT, true);
@@ -1088,6 +1498,33 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
}
/*
+ * UpdateBlockInLineInfo - Update the line information.
+ */
+static pg_attribute_always_inline int
+UpdateBlockInLineInfo(CopyState cstate, uint32 blk_pos,
+ uint32 offset, uint32 line_size, uint32 line_state)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ ParallelCopyLineBoundary *lineInfo;
+ int line_pos = lineBoundaryPtr->pos;
+
+ /* Update the line information for the worker to pick and process. */
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ while (pg_atomic_read_u32(&lineInfo->line_size) != -1)
+ COPY_WAIT_TO_PROCESS()
+
+ lineInfo->first_block = blk_pos;
+ lineInfo->start_offset = offset;
+ lineInfo->cur_lineno = cstate->cur_lineno;
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, line_state);
+ lineBoundaryPtr->pos = (lineBoundaryPtr->pos + 1) % RINGSIZE;
+
+ return line_pos;
+}
+
+/*
* ParallelCopyFrom - parallel copy leader's functionality.
*
* Leader executes the before statement for before statement trigger, if before
@@ -1110,8 +1547,302 @@ ParallelCopyFrom(CopyState cstate)
ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
ereport(DEBUG1, (errmsg("Running parallel copy leader")));
+ /* raw_buf is not used in parallel copy, instead data blocks are used.*/
+ pfree(cstate->raw_buf);
+ cstate->raw_buf = NULL;
+
+ /* Execute the before statement triggers from the leader */
+ ExecBeforeStmtTrigger(cstate);
+
+ /* On input just throw the header line away. */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ {
+ pcshared_info->is_read_in_progress = false;
+ return; /* done */
+ }
+ }
+
+ for (;;)
+ {
+ bool done;
+ cstate->cur_lineno++;
+
+ /* Actually read the line into memory here. */
+ done = CopyReadLine(cstate);
+
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ break;
+ }
+
pcshared_info->is_read_in_progress = false;
cstate->cur_lineno = 0;
+ }
+
+/*
+ * GetLinePosition - Return the line position that worker should process.
+ */
+static uint32
+GetLinePosition(CopyState cstate)
+{
+ ParallelCopyData *pcdata = cstate->pcdata;
+ ParallelCopyShmInfo *pcshared_info = pcdata->pcshared_info;
+ uint32 previous_pos = pcdata->worker_processed_pos;
+ uint32 write_pos = (previous_pos == -1) ? 0 : (previous_pos + 1) % RINGSIZE;
+ for (;;)
+ {
+ int dataSize;
+ bool is_read_in_progress = pcshared_info->is_read_in_progress;
+ ParallelCopyLineBoundary *lineInfo;
+ ParallelCopyDataBlock *data_blk_ptr;
+ ParallelCopyLineState line_state = LINE_LEADER_POPULATED;
+ ParallelCopyLineState curr_line_state;
+ CHECK_FOR_INTERRUPTS();
+
+ /* File read completed & no elements to process. */
+ if (!is_read_in_progress &&
+ (pcshared_info->populated ==
+ pg_atomic_read_u64(&pcshared_info->total_worker_processed)))
+ {
+ write_pos = -1;
+ break;
+ }
+
+ /* Get the current line information. */
+ lineInfo = &pcshared_info->line_boundaries.ring[write_pos];
+ curr_line_state = pg_atomic_read_u32(&lineInfo->line_state);
+ if ((write_pos % WORKER_CHUNK_COUNT == 0) &&
+ (curr_line_state == LINE_WORKER_PROCESSED ||
+ curr_line_state == LINE_WORKER_PROCESSING))
+ {
+ pcdata->worker_processed_pos = write_pos;
+ write_pos = (write_pos + WORKER_CHUNK_COUNT) % RINGSIZE;
+ continue;
+ }
+
+ /* Get the size of this line. */
+ dataSize = pg_atomic_read_u32(&lineInfo->line_size);
+
+ if (dataSize != 0) /* If not an empty line. */
+ {
+ /* Get the block information. */
+ data_blk_ptr = &pcshared_info->data_blocks[lineInfo->first_block];
+
+ if (!data_blk_ptr->curr_blk_completed && (dataSize == -1))
+ {
+ /* Wait till the current line or block is added. */
+ COPY_WAIT_TO_PROCESS()
+ continue;
+ }
+ }
+
+ /* Make sure that no worker has consumed this element. */
+ if (pg_atomic_compare_exchange_u32(&lineInfo->line_state,
+ &line_state, LINE_WORKER_PROCESSING))
+ break;
+ }
+
+ pcdata->worker_processed_pos = write_pos;
+ return write_pos;
+}
+
+/*
+ * GetFreeCopyBlock - Get a free block for data to be copied.
+ */
+static pg_attribute_always_inline uint32
+GetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ int count = 0;
+ uint32 last_free_block = pcshared_info->cur_block_pos;
+ uint32 block_pos = (last_free_block != -1) ? ((last_free_block + 1) % MAX_BLOCKS_COUNT): 0;
+
+ /*
+ * Get a new block for copying data, don't check current block, current
+ * block will have some unprocessed data.
+ */
+ while (count < (MAX_BLOCKS_COUNT - 1))
+ {
+ ParallelCopyDataBlock *dataBlkPtr = &pcshared_info->data_blocks[block_pos];
+ uint32 unprocessed_line_parts = pg_atomic_read_u32(&dataBlkPtr->unprocessed_line_parts);
+ if (unprocessed_line_parts == 0)
+ {
+ dataBlkPtr->curr_blk_completed = false;
+ dataBlkPtr->skip_bytes = 0;
+ pcshared_info->cur_block_pos = block_pos;
+ return block_pos;
+ }
+
+ block_pos = (block_pos + 1) % MAX_BLOCKS_COUNT;
+ count++;
+ }
+
+ return -1;
+}
+
+/*
+ * WaitGetFreeCopyBlock - If there are no blocks available, wait and get a block
+ * for copying data.
+ */
+static uint32
+WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
+{
+ uint32 new_free_pos = -1;
+ for (;;)
+ {
+ new_free_pos = GetFreeCopyBlock(pcshared_info);
+ if (new_free_pos != -1) /* We have got one block, break now. */
+ break;
+
+ COPY_WAIT_TO_PROCESS()
+ }
+
+ return new_free_pos;
+}
+
+/*
+ * SetRawBufForLoad - Set raw_buf to the shared memory where the file data must
+ * be read.
+ */
+static void
+SetRawBufForLoad(CopyState cstate, uint32 line_size, uint32 copy_buf_len,
+ uint32 raw_buf_ptr, char **copy_raw_buf)
+{
+ ParallelCopyShmInfo *pcshared_info;
+ uint32 cur_block_pos;
+ uint32 next_block_pos;
+ ParallelCopyDataBlock *cur_data_blk_ptr = NULL;
+ ParallelCopyDataBlock *next_data_blk_ptr = NULL;
+
+ if (!IsParallelCopy())
+ return;
+
+ pcshared_info = cstate->pcdata->pcshared_info;
+ cur_block_pos = pcshared_info->cur_block_pos;
+ cur_data_blk_ptr = (cstate->raw_buf) ? &pcshared_info->data_blocks[cur_block_pos] : NULL;
+ next_block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ next_data_blk_ptr = &pcshared_info->data_blocks[next_block_pos];
+
+ /* set raw_buf to the data block in shared memory */
+ cstate->raw_buf = next_data_blk_ptr->data;
+ *copy_raw_buf = cstate->raw_buf;
+ if (cur_data_blk_ptr && line_size)
+ {
+ /*
+ * Mark the previous block as completed, worker can start copying this
+ * data.
+ */
+ cur_data_blk_ptr->following_block = next_block_pos;
+ pg_atomic_add_fetch_u32(&cur_data_blk_ptr->unprocessed_line_parts, 1);
+ cur_data_blk_ptr->skip_bytes = copy_buf_len - raw_buf_ptr;
+ cur_data_blk_ptr->curr_blk_completed = true;
+ }
+}
+
+/*
+ * EndLineParallelCopy - Update the line information in shared memory.
+ */
+static void
+EndLineParallelCopy(CopyState cstate, uint32 line_pos, uint32 line_size,
+ uint32 raw_buf_ptr)
+{
+ uint8 new_line_size;
+ if (!IsParallelCopy())
+ return;
+
+ if (!IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
+ SET_NEWLINE_SIZE()
+ if (line_size)
+ {
+ ParallelCopyLineBoundary *lineInfo = &lineBoundaryPtr->ring[line_pos];
+ /*
+ * If the new_line_size > raw_buf_ptr, then the new block has only
+ * new line char content. The unprocessed count should not be
+ * increased in this case.
+ */
+ if (raw_buf_ptr > new_line_size)
+ {
+ uint32 cur_block_pos = pcshared_info->cur_block_pos;
+ ParallelCopyDataBlock *curr_data_blk_ptr = &pcshared_info->data_blocks[cur_block_pos];
+ pg_atomic_add_fetch_u32(&curr_data_blk_ptr->unprocessed_line_parts, 1);
+ }
+
+ /* Update line size. */
+ pg_atomic_write_u32(&lineInfo->line_size, line_size);
+ pg_atomic_write_u32(&lineInfo->line_state, LINE_LEADER_POPULATED);
+ elog(DEBUG1, "[Leader] After adding - line position:%d, line_size:%d",
+ line_pos, line_size);
+ pcshared_info->populated++;
+ }
+ else if (new_line_size)
+ {
+ /* This means only new line char, empty record should be inserted.*/
+ ParallelCopyLineBoundary *lineInfo;
+ line_pos = UpdateBlockInLineInfo(cstate, -1, -1, 0,
+ LINE_LEADER_POPULATED);
+ lineInfo = &lineBoundaryPtr->ring[line_pos];
+ elog(DEBUG1, "[Leader] Added empty line with offset:%d, line position:%d, line size:%d",
+ lineInfo->start_offset, line_pos,
+ pg_atomic_read_u32(&lineInfo->line_size));
+ pcshared_info->populated++;
+ }
+ }
+}
+
+/*
+ * ExecBeforeStmtTrigger - Execute the before statement trigger, this will be
+ * executed for parallel copy by the leader process.
+ */
+static void
+ExecBeforeStmtTrigger(CopyState cstate)
+{
+ EState *estate = CreateExecutorState();
+ ResultRelInfo *resultRelInfo;
+
+ Assert(IsLeader());
+
+ /*
+ * We need a ResultRelInfo so we can use the regular executor's
+ * index-entry-making machinery. (There used to be a huge amount of code
+ * here that basically duplicated execUtils.c ...)
+ */
+ resultRelInfo = makeNode(ResultRelInfo);
+ InitResultRelInfo(resultRelInfo,
+ cstate->rel,
+ 1, /* must match rel's position in range_table */
+ NULL,
+ 0);
+
+ /* Verify the named relation is a valid target for INSERT */
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
+
+ estate->es_result_relations = resultRelInfo;
+ estate->es_num_result_relations = 1;
+ estate->es_result_relation_info = resultRelInfo;
+
+ ExecInitRangeTable(estate, cstate->range_table);
+
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+
+ /* Close any trigger target relations */
+ ExecCleanUpTriggerState(estate);
+
+ FreeExecutorState(estate);
}
/*
@@ -3573,7 +4304,8 @@ CopyFrom(CopyState cstate)
PartitionTupleRouting *proute = NULL;
ErrorContextCallback errcallback;
- CommandId mycid = GetCurrentCommandId(true);
+ CommandId mycid = IsParallelCopy() ? GetCurrentCommandId(false) :
+ GetCurrentCommandId(true);
int ti_options = 0; /* start with default options for insert */
BulkInsertState bistate = NULL;
CopyInsertMethod insertMethod;
@@ -3583,7 +4315,14 @@ CopyFrom(CopyState cstate)
bool has_instead_insert_row_trig;
bool leafpart_use_multi_insert = false;
- CheckTargetRelValidity(cstate);
+ /*
+ * Perform this check if it is not parallel copy. In case of parallel
+ * copy, this check is done by the leader, so that if any invalid case
+ * exist the copy from command will error out from the leader itself,
+ * avoiding launching workers, just to throw error.
+ */
+ if (!IsParallelCopy())
+ CheckTargetRelValidity(cstate);
/*
* If the target file is new-in-transaction, we assume that checking FSM
@@ -3623,7 +4362,8 @@ CopyFrom(CopyState cstate)
target_resultRelInfo = resultRelInfo;
/* Verify the named relation is a valid target for INSERT */
- CheckValidResultRel(resultRelInfo, CMD_INSERT);
+ if (!IsParallelCopy())
+ CheckValidResultRel(resultRelInfo, CMD_INSERT);
ExecOpenIndices(resultRelInfo, false);
@@ -3772,13 +4512,16 @@ CopyFrom(CopyState cstate)
has_instead_insert_row_trig = (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_instead_row);
- /*
- * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
- * should do this for COPY, since it's not really an "INSERT" statement as
- * such. However, executing these triggers maintains consistency with the
- * EACH ROW triggers that we already fire on COPY.
- */
- ExecBSInsertTriggers(estate, resultRelInfo);
+ if (!IsParallelCopy())
+ {
+ /*
+ * Check BEFORE STATEMENT insertion triggers. It's debatable whether we
+ * should do this for COPY, since it's not really an "INSERT" statement as
+ * such. However, executing these triggers maintains consistency with the
+ * EACH ROW triggers that we already fire on COPY.
+ */
+ ExecBSInsertTriggers(estate, resultRelInfo);
+ }
econtext = GetPerTupleExprContext(estate);
@@ -3878,6 +4621,16 @@ CopyFrom(CopyState cstate)
!has_instead_insert_row_trig &&
resultRelInfo->ri_FdwRoutine == NULL;
+ /*
+ * If the table has any partitions that are either foreign or
+ * has BEFORE/INSTEAD OF triggers, we can't perform copy
+ * operations with parallel workers.
+ */
+ if (!leafpart_use_multi_insert && IsParallelWorker())
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot perform PARALLEL COPY if partition has BEFORE/INSTEAD OF triggers, or if the partition is foreign partition"),
+ errhint("Try COPY without PARALLEL option")));
+
/* Set the multi-insert buffer to use for this partition. */
if (leafpart_use_multi_insert)
{
@@ -4294,7 +5047,7 @@ BeginCopyFrom(ParseState *pstate,
* only in text mode.
*/
initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf = (IsParallelCopy()) ? NULL : (char *) palloc(RAW_BUF_SIZE + 1);
cstate->raw_buf_index = cstate->raw_buf_len = 0;
if (!cstate->binary)
{
@@ -4443,26 +5196,35 @@ NextCopyFromRawFields(CopyState cstate, char ***fields, int *nfields)
/* only available for text or csv input */
Assert(!cstate->binary);
- /* on input just throw the header line away */
- if (cstate->cur_lineno == 0 && cstate->header_line)
+ if (IsParallelCopy())
{
- cstate->cur_lineno++;
- if (CopyReadLine(cstate))
- return false; /* done */
+ done = GetWorkerLine(cstate);
+ if (done && cstate->line_buf.len == 0)
+ return false;
}
+ else
+ {
+ /* on input just throw the header line away */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
+ {
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ return false; /* done */
+ }
- cstate->cur_lineno++;
+ cstate->cur_lineno++;
- /* Actually read the line into memory here */
- done = CopyReadLine(cstate);
+ /* Actually read the line into memory here */
+ done = CopyReadLine(cstate);
- /*
- * EOF at start of line means we're done. If we see EOF after some
- * characters, we act as though it was newline followed by EOF, ie,
- * process the line and then exit loop on next iteration.
- */
- if (done && cstate->line_buf.len == 0)
- return false;
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ return false;
+ }
/* Parse the line into de-escaped field values */
if (cstate->csv_mode)
@@ -4710,9 +5472,31 @@ CopyReadLine(CopyState cstate)
*/
if (cstate->copy_dest == COPY_NEW_FE)
{
+ bool bIsFirst = true;
do
{
- cstate->raw_buf_index = cstate->raw_buf_len;
+ if (!IsParallelCopy())
+ cstate->raw_buf_index = cstate->raw_buf_len;
+ else
+ {
+ if (cstate->raw_buf_index == RAW_BUF_SIZE)
+ {
+ /* Get a new block if it is the first time, From the
+ * subsequent time, reset the index and re-use the same
+ * block.
+ */
+ if (bIsFirst)
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ uint32 block_pos = WaitGetFreeCopyBlock(pcshared_info);
+ cstate->raw_buf = pcshared_info->data_blocks[block_pos].data;
+ bIsFirst = false;
+ }
+
+ cstate->raw_buf_index = cstate->raw_buf_len = 0;
+ }
+ }
+
} while (CopyLoadRawBuf(cstate));
}
}
@@ -4767,7 +5551,7 @@ static void
ConvertToServerEncoding(CopyState cstate)
{
/* Done reading the line. Convert it to server encoding. */
- if (cstate->need_transcoding)
+ if (cstate->need_transcoding && (!IsParallelCopy() || IsWorker()))
{
char *cvt;
cvt = pg_any_to_server(cstate->line_buf.data,
@@ -4806,6 +5590,11 @@ CopyReadLineText(CopyState cstate)
char quotec = '\0';
char escapec = '\0';
+ /* For parallel copy */
+ int line_size = 0;
+ int line_pos = 0;
+
+ cstate->eol_type = EOL_UNKNOWN;
if (cstate->csv_mode)
{
quotec = cstate->quote[0];
@@ -4860,6 +5649,8 @@ CopyReadLineText(CopyState cstate)
if (raw_buf_ptr >= copy_buf_len || need_data)
{
REFILL_LINEBUF;
+ SetRawBufForLoad(cstate, line_size, copy_buf_len, raw_buf_ptr,
+ ©_raw_buf);
/*
* Try to read some more data. This will certainly reset
@@ -5084,9 +5875,15 @@ CopyReadLineText(CopyState cstate)
* discard the data and the \. sequence.
*/
if (prev_raw_ptr > cstate->raw_buf_index)
- appendBinaryStringInfo(&cstate->line_buf,
+ {
+ if (!IsParallelCopy())
+ appendBinaryStringInfo(&cstate->line_buf,
cstate->raw_buf + cstate->raw_buf_index,
prev_raw_ptr - cstate->raw_buf_index);
+ else
+ line_size += prev_raw_ptr - cstate->raw_buf_index;
+ }
+
cstate->raw_buf_index = raw_buf_ptr;
result = true; /* report EOF */
break;
@@ -5138,6 +5935,26 @@ not_end_of_copy:
IF_NEED_REFILL_AND_EOF_BREAK(mblen - 1);
raw_buf_ptr += mblen - 1;
}
+
+ /*
+ * Skip the header line. Update the line here, this cannot be done at
+ * the beginning, as there is a possibility that file contains empty
+ * lines.
+ */
+ if (IsParallelCopy() && first_char_in_line && !IsHeaderLine())
+ {
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ ParallelCopyLineBoundary *lineInfo;
+ uint32 line_first_block = pcshared_info->cur_block_pos;
+ line_pos = UpdateBlockInLineInfo(cstate,
+ line_first_block,
+ cstate->raw_buf_index, -1,
+ LINE_LEADER_POPULATING);
+ lineInfo = &pcshared_info->line_boundaries.ring[line_pos];
+ elog(DEBUG1, "[Leader] Adding - block:%d, offset:%d, line position:%d",
+ line_first_block, lineInfo->start_offset, line_pos);
+ }
+
first_char_in_line = false;
} /* end of outer loop */
@@ -5146,6 +5963,7 @@ not_end_of_copy:
*/
REFILL_LINEBUF;
CLEAR_EOL_LINE();
+ EndLineParallelCopy(cstate, line_pos, line_size, raw_buf_ptr);
return result;
}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9c6f5ec..43fc823 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2353,7 +2353,7 @@ psql_completion(const char *text, int start, int end)
/* Complete COPY <sth> FROM|TO filename WITH ( */
else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "("))
COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
- "HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
+ "HEADER", "PARALLEL", "QUOTE", "ESCAPE", "FORCE_QUOTE",
"FORCE_NOT_NULL", "FORCE_NULL", "ENCODING");
/* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index df1b43a..71a6c9b 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -385,6 +385,8 @@ extern FullTransactionId GetTopFullTransactionId(void);
extern FullTransactionId GetTopFullTransactionIdIfAny(void);
extern FullTransactionId GetCurrentFullTransactionId(void);
extern FullTransactionId GetCurrentFullTransactionIdIfAny(void);
+extern void AssignFullTransactionIdForWorker(FullTransactionId fullTransactionId);
+extern void AssignCommandIdForWorker(CommandId commandId, bool used);
extern void MarkCurrentTransactionIdLoggedIfAny(void);
extern bool SubTransactionIsActive(SubTransactionId subxid);
extern CommandId GetCurrentCommandId(bool used);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 509c695..e8d8ffd 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1707,6 +1707,7 @@ ParallelCopyLineBoundary
ParallelCopyData
ParallelCopyDataBlock
ParallelCopyLineBuf
+ParallelCopyLineState
ParallelCopyShmInfo
ParallelExecutorInfo
ParallelHashGrowth
--
1.8.3.1
[text/x-patch] v5-0004-Documentation-for-parallel-copy.patch (2.0K, ../../CALDaNm2rRNnLoC0g9z9aDzpz1FoAanv5u00ri1d+E8Y3of5rxg@mail.gmail.com/5-v5-0004-Documentation-for-parallel-copy.patch)
download | inline diff:
From 6a04ea9b0fbd62966d37212c7590f62c9d71b309 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:00:36 +0530
Subject: [PATCH v5 4/6] Documentation for parallel copy.
This patch has the documentation changes for parallel copy.
---
doc/src/sgml/ref/copy.sgml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index 18189ab..2e023ed 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -37,6 +37,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
DELIMITER '<replaceable class="parameter">delimiter_character</replaceable>'
NULL '<replaceable class="parameter">null_string</replaceable>'
HEADER [ <replaceable class="parameter">boolean</replaceable> ]
+ PARALLEL <replaceable class="parameter">integer</replaceable>
QUOTE '<replaceable class="parameter">quote_character</replaceable>'
ESCAPE '<replaceable class="parameter">escape_character</replaceable>'
FORCE_QUOTE { ( <replaceable class="parameter">column_name</replaceable> [, ...] ) | * }
@@ -275,6 +276,22 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
</varlistentry>
<varlistentry>
+ <term><literal>PARALLEL</literal></term>
+ <listitem>
+ <para>
+ Perform <command>COPY FROM</command> in parallel using <replaceable
+ class="parameter"> integer</replaceable> background workers. Please
+ note that it is not guaranteed that the number of parallel workers
+ specified in <replaceable class="parameter">integer</replaceable> will
+ be used during execution. It is possible for a copy to run with fewer
+ workers than specified, or even with no workers at all (for example,
+ due to the setting of max_worker_processes). This option is allowed
+ only in <command>COPY FROM</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>QUOTE</literal></term>
<listitem>
<para>
--
1.8.3.1
[text/x-patch] v5-0005-Tests-for-parallel-copy.patch (19.7K, ../../CALDaNm2rRNnLoC0g9z9aDzpz1FoAanv5u00ri1d+E8Y3of5rxg@mail.gmail.com/6-v5-0005-Tests-for-parallel-copy.patch)
download | inline diff:
From f2b0635303b986ea944f38e9325b1f06c53a5060 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Sat, 1 Aug 2020 09:19:39 +0530
Subject: [PATCH v5 5/6] Tests for parallel copy.
This patch has the tests for parallel copy.
---
src/test/regress/expected/copy2.out | 205 ++++++++++++++++++++++++++++++++++-
src/test/regress/input/copy.source | 12 +++
src/test/regress/output/copy.source | 12 +++
src/test/regress/sql/copy2.sql | 208 +++++++++++++++++++++++++++++++++++-
4 files changed, 429 insertions(+), 8 deletions(-)
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index e40287d..7ae5d44 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -254,18 +254,32 @@ It is "perfect".|
"It is ""perfect""."," "
"",
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+SELECT * FROM testnl;
+ a | b | c
+---+----------------------+---
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+ 1 | a field with two LFs+| 2
+ | +|
+ | inside |
+(2 rows)
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
COPY testeoc TO stdout CSV;
a\.
\.b
c\.d
"\."
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
1 \\0
@@ -280,6 +294,15 @@ SELECT * FROM testnull;
|
(4 rows)
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+SELECT * FROM testnull;
+ a | b
+----+----
+ 42 | \0
+ |
+(2 rows)
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -349,6 +372,34 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ a2
+ b
+(2 rows)
+
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+COMMIT;
+SELECT * FROM vistest;
+ a
+----
+ d2
+ e
+(2 rows)
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
SELECT * FROM vistest;
a
@@ -409,7 +460,7 @@ SELECT * FROM vistest;
(2 rows)
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -420,6 +471,8 @@ CREATE TEMP TABLE forcetest (
-- should succeed with no effect ("b" remains an empty string, "c" remains NULL)
BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
b | c
@@ -430,6 +483,8 @@ SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
c | d
@@ -486,6 +541,31 @@ select * from check_con_tbl;
(2 rows)
+\d+ check_con_tbl
+ Table "public.check_con_tbl"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | | | plain | |
+Check constraints:
+ "check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
+
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":1}
+NOTICE: input = {"f1":null}
+copy check_con_tbl from stdin with (parallel 1);
+NOTICE: input = {"f1":0}
+ERROR: new row for relation "check_con_tbl" violates check constraint "check_con_tbl_check"
+DETAIL: Failing row contains (0).
+CONTEXT: COPY check_con_tbl, line 1: "0"
+parallel worker
+select * from check_con_tbl;
+ f1
+----
+ 1
+
+(2 rows)
+
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
CREATE ROLE regress_rls_copy_user_colperms;
@@ -600,8 +680,125 @@ SELECT * FROM instead_of_insert_tbl;
(2 rows)
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+ERROR: value 0 out of bounds for option "parallel"
+DETAIL: Valid values are between "1" and "1024".
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+COPY temp_test (a) from stdin with (PARALLEL 1);
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+ERROR: column "xyz" of relation "test_parallel_copy" does not exist
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+ERROR: column "d" specified more than once
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: invalid input syntax for type integer: ""
+CONTEXT: COPY test_parallel_copy, line 0, column a: ""
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2000 230 23 23"
+parallel worker
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: missing data for column "e"
+CONTEXT: COPY test_parallel_copy, line 1: "2001 231 \N \N"
+parallel worker
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+ERROR: extra data after last expected column
+CONTEXT: COPY test_parallel_copy, line 1: "2002 232 40 50 60 70 80"
+parallel worker
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ERROR: column "f" does not exist
+LINE 1: ..._parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ERROR: missing FROM-clause entry for table "x"
+LINE 1: ...rallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+ERROR: cannot use subquery in COPY FROM WHERE condition
+LINE 1: ...arallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT...
+ ^
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+ERROR: set-returning functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...lel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_s...
+ ^
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+ERROR: window functions are not allowed in COPY FROM WHERE conditions
+LINE 1: ...rallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number...
+ ^
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+ a | b | c | d | e
+-------+----+------------+---------+---------
+ 1 | 11 | test_c1 | test_d1 | test_e1
+ 2 | 12 | test_c2 | test_d2 | test_e2
+ | 3 | stuff | test_d3 |
+ | 4 | stuff | test_d4 |
+ | 5 | stuff | test_d5 |
+ | | 45 | 80 | 90
+ | | x | \x | \x
+ | | , | \, | \
+ 3000 | | c | |
+ 4000 | | C | |
+ 4001 | 1 | empty | |
+ 4002 | 2 | null | |
+ 4003 | 3 | Backslash | \ | \
+ 4004 | 4 | BackslashX | \X | \X
+ 4005 | 5 | N | N | N
+ 4006 | 6 | BackslashN | \N | \N
+ 4007 | 7 | XX | XX | XX
+ 4008 | 8 | Delimiter | : | :
+ 50004 | 25 | 35 | 45 | 55
+ 60004 | 25 | 35 | 45 | 55
+ 60005 | 26 | 36 | 46 | 56
+(21 rows)
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source
index a1d529a..159c058 100644
--- a/src/test/regress/input/copy.source
+++ b/src/test/regress/input/copy.source
@@ -15,6 +15,13 @@ DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+
+SELECT COUNT(*) FROM tenk1;
+
+TRUNCATE tenk1;
+
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
@@ -159,6 +166,11 @@ truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
+
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source
index 938d355..c3003fe 100644
--- a/src/test/regress/output/copy.source
+++ b/src/test/regress/output/copy.source
@@ -9,6 +9,15 @@ COPY onek FROM '@abs_srcdir@/data/onek.data';
COPY onek TO '@abs_builddir@/results/onek.data';
DELETE FROM onek;
COPY onek FROM '@abs_builddir@/results/onek.data';
+-- test parallel copy
+COPY tenk1 FROM '@abs_srcdir@/data/tenk.data' WITH (parallel 2);
+SELECT COUNT(*) FROM tenk1;
+ count
+-------
+ 10000
+(1 row)
+
+TRUNCATE tenk1;
COPY tenk1 FROM '@abs_srcdir@/data/tenk.data';
COPY slow_emp4000 FROM '@abs_srcdir@/data/rect.data';
COPY person FROM '@abs_srcdir@/data/person.data';
@@ -113,6 +122,9 @@ insert into parted_copytest select x,1,'One' from generate_series(1011,1020) x;
copy (select * from parted_copytest order by a) to '@abs_builddir@/results/parted_copytest.csv';
truncate parted_copytest;
copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Test parallel copy from with a partitioned table.
+truncate parted_copytest;
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' with (parallel 2);
-- Ensure COPY FREEZE errors for partitioned tables.
begin;
truncate parted_copytest;
diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql
index 902f4fa..7015698 100644
--- a/src/test/regress/sql/copy2.sql
+++ b/src/test/regress/sql/copy2.sql
@@ -157,7 +157,7 @@ COPY y TO stdout (FORMAT CSV, FORCE_QUOTE *);
--test that we read consecutive LFs properly
-CREATE TEMP TABLE testnl (a int, b text, c int);
+CREATE TABLE testnl (a int, b text, c int);
COPY testnl FROM stdin CSV;
1,"a field with two LFs
@@ -165,8 +165,16 @@ COPY testnl FROM stdin CSV;
inside",2
\.
+COPY testnl FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+1,"a field with two LFs
+
+inside",2
+\.
+
+SELECT * FROM testnl;
+
-- test end of copy marker
-CREATE TEMP TABLE testeoc (a text);
+CREATE TABLE testeoc (a text);
COPY testeoc FROM stdin CSV;
a\.
@@ -175,11 +183,19 @@ c\.d
"\."
\.
+TRUNCATE testeoc;
+COPY testeoc FROM stdin WITH (FORMAT 'csv', PARALLEL 1);
+a\.
+\.b
+c\.d
+"\."
+\.
+
COPY testeoc TO stdout CSV;
-- test handling of nonstandard null marker that violates escaping rules
-CREATE TEMP TABLE testnull(a int, b text);
+CREATE TABLE testnull(a int, b text);
INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL);
COPY testnull TO stdout WITH NULL AS E'\\0';
@@ -191,6 +207,14 @@ COPY testnull FROM stdin WITH NULL AS E'\\0';
SELECT * FROM testnull;
+TRUNCATE testnull;
+COPY testnull FROM stdin WITH (NULL E'\\0', PARALLEL 1);
+42 \\0
+\0 \0
+\.
+
+SELECT * FROM testnull;
+
BEGIN;
CREATE TABLE vistest (LIKE testeoc);
COPY vistest FROM stdin CSV;
@@ -235,6 +259,23 @@ SELECT * FROM vistest;
BEGIN;
TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+a2
+b
+\.
+SELECT * FROM vistest;
+SAVEPOINT s1;
+TRUNCATE vistest;
+COPY vistest FROM stdin WITH (FORMAT 'csv', FREEZE, PARALLEL 1);
+d2
+e
+\.
+SELECT * FROM vistest;
+COMMIT;
+SELECT * FROM vistest;
+
+BEGIN;
+TRUNCATE vistest;
COPY vistest FROM stdin CSV FREEZE;
x
y
@@ -284,7 +325,7 @@ SELECT * FROM vistest;
COMMIT;
SELECT * FROM vistest;
-- Test FORCE_NOT_NULL and FORCE_NULL options
-CREATE TEMP TABLE forcetest (
+CREATE TABLE forcetest (
a INT NOT NULL,
b TEXT NOT NULL,
c TEXT,
@@ -297,6 +338,10 @@ BEGIN;
COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c));
1,,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c), PARALLEL 1);
+1,,""
+\.
COMMIT;
SELECT b, c FROM forcetest WHERE a = 1;
-- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified
@@ -304,6 +349,10 @@ BEGIN;
COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d));
2,'a',,""
\.
+TRUNCATE forcetest;
+COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d), PARALLEL 1);
+2,'a',,""
+\.
COMMIT;
SELECT c, d FROM forcetest WHERE a = 2;
-- should fail with not-null constraint violation
@@ -339,6 +388,16 @@ copy check_con_tbl from stdin;
0
\.
select * from check_con_tbl;
+\d+ check_con_tbl
+truncate check_con_tbl;
+copy check_con_tbl from stdin with (parallel 1);
+1
+\N
+\.
+copy check_con_tbl from stdin with (parallel 1);
+0
+\.
+select * from check_con_tbl;
-- test with RLS enabled.
CREATE ROLE regress_rls_copy_user;
@@ -440,8 +499,149 @@ test1
SELECT * FROM instead_of_insert_tbl;
COMMIT;
+-- Parallel copy tests.
+CREATE TABLE test_parallel_copy (
+ a INT,
+ b INT,
+ c TEXT not null default 'stuff',
+ d TEXT,
+ e TEXT
+) ;
+
+COPY test_parallel_copy (a, b, c, d, e) from stdin with (PARALLEL 1);
+1 11 test_c1 test_d1 test_e1
+2 12 test_c2 test_d2 test_e2
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+3 test_d3
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1);
+4 test_d4
+5 test_d5
+\.
+
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL 1, FORMAT 'csv', HEADER);
+b d
+\.
+
+-- zero workers: should perform non-parallel copy
+COPY test_parallel_copy (b, d) from stdin with (PARALLEL '0');
+
+-- referencing table: should perform non-parallel copy
+CREATE TABLE test_copy_pk(c1 INT PRIMARY KEY);
+INSERT INTO test_copy_pk VALUES(10);
+CREATE TABLE test_copy_ri(c1 INT REFERENCES test_copy_pk(c1));
+COPY test_copy_ri from stdin with (FORMAT csv, DELIMITER ',', PARALLEL 1);
+10
+\.
+
+-- expressions: should perform non-parallel copy
+CREATE TABLE test_copy_expr (index INT, height REAL, weight REAL);
+
+COPY test_copy_expr FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1) WHERE height > random() * 65;
+60,60,60
+\.
+
+-- serial data: should perform non-parallel copy
+CREATE TABLE testserial (index SERIAL, height REAL);
+
+COPY testserial(height) FROM STDIN WITH (FORMAT csv, DELIMITER ',', PARALLEL 1);
+60
+\.
+
+-- temporary table copy: should perform non-parallel copy
+CREATE TEMPORARY TABLE temp_test(
+ a int
+) ;
+
+COPY temp_test (a) from stdin with (PARALLEL 1);
+10
+\.
+
+-- non-existent column in column list: should fail
+COPY test_parallel_copy (xyz) from stdin with (PARALLEL 1);
+
+-- too many columns in column list: should fail
+COPY test_parallel_copy (a, b, c, d, e, d, c) from stdin with (PARALLEL 1);
+
+-- missing data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2000 230 23 23
+\.
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2001 231 \N \N
+\.
+
+-- extra data: should fail
+COPY test_parallel_copy from stdin with (PARALLEL 1);
+2002 232 40 50 60 70 80
+\.
+
+-- various COPY options: delimiters, oids, NULL string, encoding
+COPY test_parallel_copy (b, c, d, e) from stdin with (delimiter ',', null 'x', PARALLEL 1) ;
+x,45,80,90
+x,\x,\\x,\\\x
+x,\,,\\\,,\\
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ';', NULL '', PARALLEL 1);
+3000;;c;;
+\.
+
+COPY test_parallel_copy from stdin WITH (DELIMITER ':', NULL E'\\X', ENCODING 'sql_ascii', PARALLEL 1);
+4000:\X:C:\X:\X
+4001:1:empty::
+4002:2:null:\X:\X
+4003:3:Backslash:\\:\\
+4004:4:BackslashX:\\X:\\X
+4005:5:N:\N:\N
+4006:6:BackslashN:\\N:\\N
+4007:7:XX:\XX:\XX
+4008:8:Delimiter:\::\:
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = 50004;
+50003 24 34 44 54
+50004 25 35 45 55
+50005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a > 60003;
+60001 22 32 42 52
+60002 23 33 43 53
+60003 24 34 44 54
+60004 25 35 45 55
+60005 26 36 46 56
+\.
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE f > 60003;
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a = max(x.b);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (SELECT 1 FROM x);
+
+COPY test_parallel_copy from stdin WITH (PARALLEL 1) WHERE a IN (generate_series(1,5));
+
+COPY test_parallel_copy from stdin WITH(PARALLEL 1) WHERE a = row_number() over(b);
+
+-- check results of copy in
+SELECT * FROM test_parallel_copy;
+
-- clean up
+DROP TABLE test_copy_ri;
+DROP TABLE test_copy_pk;
+DROP TABLE test_copy_expr;
+DROP TABLE testeoc;
+DROP TABLE testnl;
DROP TABLE forcetest;
+DROP TABLE test_parallel_copy;
+DROP TABLE testserial;
+DROP TABLE testnull;
DROP TABLE vistest;
DROP FUNCTION truncate_in_subxact();
DROP TABLE x, y;
--
1.8.3.1
[text/x-patch] v5-0006-Parallel-Copy-For-Binary-Format-Files.patch (26.9K, ../../CALDaNm2rRNnLoC0g9z9aDzpz1FoAanv5u00ri1d+E8Y3of5rxg@mail.gmail.com/7-v5-0006-Parallel-Copy-For-Binary-Format-Files.patch)
download | inline diff:
From 8dc40e5d290edd954b7914d3f8abe3de22b1667d Mon Sep 17 00:00:00 2001
From: Vignesh C <[email protected]>
Date: Tue, 22 Sep 2020 13:43:10 +0530
Subject: [PATCH v5 6/6] Parallel Copy For Binary Format Files
Leader reads data from the file into the DSM data blocks each of 64K size.
It also identifies each tuple data block id, start offset, end offset,
tuple size and updates this information in the ring data structure.
Workers parallelly read the tuple information from the ring data structure,
the actual tuple data from the data blocks and parallelly insert the tuples
into the table.
---
src/backend/commands/copy.c | 681 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 597 insertions(+), 84 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index ba188d7..5b1884a 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -266,6 +266,17 @@ typedef struct ParallelCopyLineBuf
} ParallelCopyLineBuf;
/*
+ * Represents the usage mode for CopyReadBinaryGetDataBlock.
+ */
+typedef enum FieldInfoType
+{
+ FIELD_NONE = 0,
+ FIELD_COUNT,
+ FIELD_SIZE,
+ FIELD_DATA
+} FieldInfoType;
+
+/*
* Parallel copy data information.
*/
typedef struct ParallelCopyData
@@ -286,6 +297,9 @@ typedef struct ParallelCopyData
/* Current position in worker_line_buf */
uint32 worker_line_buf_pos;
+
+ /* For binary formatted files */
+ ParallelCopyDataBlock *curr_data_block;
} ParallelCopyData;
/*
@@ -450,6 +464,7 @@ typedef struct SerializedParallelCopyState
/* Working state for COPY FROM */
AttrNumber num_defaults;
Oid relid;
+ bool binary;
} SerializedParallelCopyState;
/* DestReceiver for COPY (query) TO */
@@ -524,7 +539,6 @@ typedef struct CopyMultiInsertInfo
int ti_options; /* table insert options */
} CopyMultiInsertInfo;
-
/*
* These macros centralize code used to process line_buf and raw_buf buffers.
* They are macros because they often do continue/break control and to avoid
@@ -652,11 +666,113 @@ if (!IsParallelCopy()) \
else \
return pg_atomic_read_u64(&cstate->pcdata->pcshared_info->processed);
+/*
+ * CHECK_FIELD_COUNT - Handles the error cases for field count
+ * for binary format files.
+ */
+#define CHECK_FIELD_COUNT \
+{\
+ if (fld_count == -1) \
+ { \
+ if (IsParallelCopy() && \
+ !IsLeader()) \
+ return true; \
+ else if (IsParallelCopy() && \
+ IsLeader()) \
+ { \
+ if (cstate->pcdata->curr_data_block->data[cstate->raw_buf_index + sizeof(fld_count)] != 0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("received copy data after EOF marker"))); \
+ return true; \
+ } \
+ else \
+ { \
+ /* \
+ * Received EOF marker. In a V3-protocol copy, wait for the \
+ * protocol-level EOF, and complain if it doesn't come \
+ * immediately. This ensures that we correctly handle CopyFail, \
+ * if client chooses to send that now. \
+ * \
+ * Note that we MUST NOT try to read more data in an old-protocol \
+ * copy, since there is no protocol-level EOF marker then. We \
+ * could go either way for copy from file, but choose to throw \
+ * error if there's data after the EOF marker, for consistency \
+ * with the new-protocol case. \
+ */ \
+ char dummy; \
+ if (cstate->copy_dest != COPY_OLD_FE && \
+ CopyReadBinaryData(cstate, &dummy, 1) > 0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("received copy data after EOF marker"))); \
+ return false; \
+ } \
+ } \
+ if (fld_count != cstate->max_fields) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("row field count is %d, expected %d", \
+ (int) fld_count, cstate->max_fields))); \
+}
+
+/*
+ * CHECK_FIELD_SIZE - Handles the error case for field size
+ * for binary format files.
+ */
+#define CHECK_FIELD_SIZE(fld_size) \
+{ \
+ if (fld_size < -1) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("invalid field size")));\
+}
+
+/*
+ * EOF_ERROR - Error statement for EOF for binary format
+ * files.
+ */
+#define EOF_ERROR \
+{ \
+ ereport(ERROR, \
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), \
+ errmsg("unexpected EOF in COPY data")));\
+}
+
+/*
+ * GET_RAW_BUF_INDEX - Calculates the raw buf index for the cases
+ * where the data spread is across multiple data blocks.
+ */
+#define GET_RAW_BUF_INDEX(raw_buf_index, fld_size, required_blks, curr_blk_bytes) \
+{ \
+ raw_buf_index = fld_size - (((required_blks - 1) * DATA_BLOCK_SIZE) + curr_blk_bytes); \
+}
+
+/*
+ * GET_REQUIRED_BLOCKS - Calculates the number of data
+ * blocks required for the cases where the data spread
+ * is across multiple data blocks.
+ */
+#define GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes) \
+{ \
+ /* \
+ * field size can spread across multiple data blocks, \
+ * calculate the number of required data blocks and try to get \
+ * those many data blocks. \
+ */ \
+ required_blks = (int32)(fld_size - curr_blk_bytes)/(int32)DATA_BLOCK_SIZE; \
+ /* \
+ * check if we need the data block for the field data \
+ * bytes that are not modulus of data block size. \
+ */ \
+ if ((fld_size - curr_blk_bytes)%DATA_BLOCK_SIZE != 0) \
+ required_blks++; \
+}
+
/* End parallel copy Macros */
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
-
/* non-export function prototypes */
static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel,
RawStmt *raw_query, Oid queryRelId, List *attnamelist,
@@ -711,6 +827,13 @@ static void ExecBeforeStmtTrigger(CopyState cstate);
static void CheckTargetRelValidity(CopyState cstate);
static void PopulateCstateCatalogInfo(CopyState cstate);
static pg_attribute_always_inline uint32 GetLinePosition(CopyState cstate);
+static uint32 WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info);
+static bool CopyReadBinaryTupleLeader(CopyState cstate);
+static void CopyReadBinaryFindTupleSize(CopyState cstate, uint32 *line_size);
+static bool CopyReadBinaryTupleWorker(CopyState cstate, Datum *values, bool *nulls);
+static Datum CopyReadBinaryAttributeWorker(CopyState cstate, FmgrInfo *flinfo,
+ Oid typioparam, int32 typmod, bool *isnull);
+static void CopyReadBinaryGetDataBlock(CopyState cstate, FieldInfoType field_info);
/*
* SerializeParallelCopyState - Copy shared_cstate using cstate information.
@@ -729,6 +852,7 @@ SerializeParallelCopyState(CopyState cstate, SerializedParallelCopyState *shared
shared_cstate->convert_selectively = cstate->convert_selectively;
shared_cstate->num_defaults = cstate->num_defaults;
shared_cstate->relid = cstate->pcdata->relid;
+ shared_cstate->binary = cstate->binary;
}
/*
@@ -888,8 +1012,8 @@ FindInsertMethod(CopyState cstate)
static pg_attribute_always_inline bool
IsParallelCopyAllowed(CopyState cstate)
{
- /* Parallel copy not allowed for frontend (2.0 protocol) & binary option. */
- if ((cstate->copy_dest == COPY_OLD_FE) || cstate->binary)
+ /* Parallel copy not allowed for frontend (2.0 protocol). */
+ if (cstate->copy_dest == COPY_OLD_FE)
return false;
/* Check if copy is into foreign table or temporary table. */
@@ -1159,6 +1283,7 @@ InitializeParallelCopyInfo(SerializedParallelCopyState *shared_cstate,
cstate->convert_selectively = shared_cstate->convert_selectively;
cstate->num_defaults = shared_cstate->num_defaults;
pcdata->relid = shared_cstate->relid;
+ cstate->binary = shared_cstate->binary;
PopulateCommonCstateInfo(cstate, tup_desc, attnamelist);
@@ -1554,32 +1679,66 @@ ParallelCopyFrom(CopyState cstate)
/* Execute the before statement triggers from the leader */
ExecBeforeStmtTrigger(cstate);
- /* On input just throw the header line away. */
- if (cstate->cur_lineno == 0 && cstate->header_line)
+ if (!cstate->binary)
{
- cstate->cur_lineno++;
- if (CopyReadLine(cstate))
+ /* On input just throw the header line away. */
+ if (cstate->cur_lineno == 0 && cstate->header_line)
{
- pcshared_info->is_read_in_progress = false;
- return; /* done */
+ cstate->cur_lineno++;
+ if (CopyReadLine(cstate))
+ {
+ pcshared_info->is_read_in_progress = false;
+ return; /* done */
+ }
}
- }
- for (;;)
- {
- bool done;
- cstate->cur_lineno++;
+ for (;;)
+ {
+ bool done;
+ cstate->cur_lineno++;
- /* Actually read the line into memory here. */
- done = CopyReadLine(cstate);
+ /* Actually read the line into memory here. */
+ done = CopyReadLine(cstate);
+ /*
+ * EOF at start of line means we're done. If we see EOF after some
+ * characters, we act as though it was newline followed by EOF, ie,
+ * process the line and then exit loop on next iteration.
+ */
+ if (done && cstate->line_buf.len == 0)
+ break;
+ }
+ }
+ else
+ {
/*
- * EOF at start of line means we're done. If we see EOF after some
- * characters, we act as though it was newline followed by EOF, ie,
- * process the line and then exit loop on next iteration.
+ * Binary Format Files.
+ * For parallel copy leader, fill in the error
+ * context information here, in case any failures
+ * while determining tuple offsets, leader
+ * would throw the errors with proper context.
*/
- if (done && cstate->line_buf.len == 0)
- break;
+ ErrorContextCallback errcallback;
+ errcallback.callback = CopyFromErrorCallback;
+ errcallback.arg = (void *) cstate;
+ errcallback.previous = error_context_stack;
+ error_context_stack = &errcallback;
+ cstate->pcdata->curr_data_block = NULL;
+ cstate->raw_buf_index = 0;
+ pcshared_info->populated = 0;
+ cstate->cur_lineno = 0;
+ cstate->max_fields = list_length(cstate->attnumlist);
+
+ for (;;)
+ {
+ bool eof = false;
+ cstate->cur_lineno++;
+
+ eof = CopyReadBinaryTupleLeader(cstate);
+
+ if (eof)
+ break;
+ }
}
pcshared_info->is_read_in_progress = false;
@@ -1587,6 +1746,354 @@ ParallelCopyFrom(CopyState cstate)
}
/*
+ * CopyReadBinaryGetDataBlock - Gets a new block, updates
+ * the current offset, calculates the skip bytes.
+ */
+static void
+CopyReadBinaryGetDataBlock(CopyState cstate, FieldInfoType field_info)
+{
+ ParallelCopyDataBlock *data_block = NULL;
+ ParallelCopyDataBlock *curr_data_block = cstate->pcdata->curr_data_block;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ uint8 move_bytes = 0;
+ uint32 block_pos;
+ uint32 prev_block_pos;
+ int read_bytes = 0;
+
+ prev_block_pos = pcshared_info->cur_block_pos;
+
+ block_pos = WaitGetFreeCopyBlock(pcshared_info);
+
+ if (field_info == FIELD_SIZE || field_info == FIELD_COUNT)
+ move_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+
+ if (curr_data_block != NULL)
+ curr_data_block->skip_bytes = move_bytes;
+
+ data_block = &pcshared_info->data_blocks[block_pos];
+
+ if (move_bytes > 0 && curr_data_block != NULL)
+ memmove(&data_block->data[0], &curr_data_block->data[cstate->raw_buf_index], move_bytes);
+
+ elog(DEBUG1, "LEADER - field info %d is spread across data blocks - moved %d bytes from current block %u to %u block",
+ field_info, move_bytes, prev_block_pos, block_pos);
+
+ read_bytes = CopyGetData(cstate, &data_block->data[move_bytes], 1, (DATA_BLOCK_SIZE - move_bytes));
+
+ if (field_info == FIELD_NONE && cstate->reached_eof)
+ return;
+
+ if (cstate->reached_eof)
+ EOF_ERROR;
+
+ elog(DEBUG1, "LEADER - bytes read from file %d", read_bytes);
+
+ if(field_info == FIELD_SIZE || field_info == FIELD_DATA)
+ {
+ ParallelCopyDataBlock *prev_data_block = NULL;
+ prev_data_block = curr_data_block;
+ prev_data_block->following_block = block_pos;
+
+ if (prev_data_block->curr_blk_completed == false)
+ prev_data_block->curr_blk_completed = true;
+
+ pg_atomic_add_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ }
+
+ cstate->pcdata->curr_data_block = data_block;
+ cstate->raw_buf_index = 0;
+}
+
+/*
+ * CopyReadBinaryTupleLeader - Leader reads data from binary formatted file
+ * to data blocks and identifies tuple boundaries/offsets so that workers
+ * can work on the data blocks data.
+ */
+static bool
+CopyReadBinaryTupleLeader(CopyState cstate)
+{
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ int16 fld_count;
+ uint32 line_size = 0;
+ uint32 start_block_pos;
+ uint32 start_offset;
+
+ if (cstate->pcdata->curr_data_block == NULL)
+ {
+ CopyReadBinaryGetDataBlock(cstate, FIELD_NONE);
+
+ /*
+ * no data is read from file here. one possibility
+ * to be here could be that the binary file just
+ * has a valid signature but nothing else.
+ */
+ if (cstate->reached_eof)
+ return true;
+ }
+
+ if ((cstate->raw_buf_index + sizeof(fld_count)) >= DATA_BLOCK_SIZE)
+ CopyReadBinaryGetDataBlock(cstate, FIELD_COUNT);
+
+ memcpy(&fld_count, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_count));
+ fld_count = (int16) pg_ntoh16(fld_count);
+ CHECK_FIELD_COUNT;
+ start_offset = cstate->raw_buf_index;
+ cstate->raw_buf_index += sizeof(fld_count);
+ line_size += sizeof(fld_count);
+ start_block_pos = pcshared_info->cur_block_pos;
+
+ CopyReadBinaryFindTupleSize(cstate, &line_size);
+
+ pg_atomic_add_fetch_u32(&cstate->pcdata->curr_data_block->unprocessed_line_parts, 1);
+
+ if (line_size > 0)
+ {
+ int line_pos = UpdateBlockInLineInfo(cstate,
+ start_block_pos,
+ start_offset,
+ line_size,
+ LINE_LEADER_POPULATED);
+
+ pcshared_info->populated++;
+ elog(DEBUG1, "LEADER - adding - block:%u, offset:%u, line size:%u line position:%d",
+ start_block_pos, start_offset, line_size, line_pos);
+ }
+
+ return false;
+}
+
+/*
+ * CopyReadBinaryFindTupleSize - Leader identifies boundaries/
+ * offsets for each attribute/column and finally results in the
+ * tuple/row size. It moves on to next data block if the attribute/
+ * column is spread across data blocks.
+ */
+static void
+CopyReadBinaryFindTupleSize(CopyState cstate, uint32 *line_size)
+{
+ int32 fld_size;
+ ListCell *cur;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ foreach(cur, cstate->attnumlist)
+ {
+ int att_num = lfirst_int(cur);
+ Form_pg_attribute att = TupleDescAttr(tup_desc, (att_num - 1));
+ cstate->cur_attname = NameStr(att->attname);
+ fld_size = 0;
+
+ if ((cstate->raw_buf_index + sizeof(fld_size)) >= DATA_BLOCK_SIZE)
+ CopyReadBinaryGetDataBlock(cstate, FIELD_SIZE);
+
+ memcpy(&fld_size, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_size));
+ cstate->raw_buf_index += sizeof(fld_size);
+ *line_size += sizeof(fld_size);
+ fld_size = (int32) pg_ntoh32(fld_size);
+
+ CHECK_FIELD_SIZE(fld_size);
+
+ *line_size += fld_size;
+
+ if ((DATA_BLOCK_SIZE - cstate->raw_buf_index) >= fld_size)
+ {
+ cstate->raw_buf_index += fld_size;
+ elog(DEBUG1, "LEADER - tuple lies in he same data block");
+ }
+ else
+ {
+ int32 required_blks = 0;
+ int32 curr_blk_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+ int i = 0;
+
+ GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes);
+
+ i = required_blks;
+
+ while(i > 0)
+ {
+ CopyReadBinaryGetDataBlock(cstate, FIELD_DATA);
+ i--;
+ }
+
+ GET_RAW_BUF_INDEX(cstate->raw_buf_index, fld_size, required_blks, curr_blk_bytes);
+
+ /*
+ * raw_buf_index should never cross data block size,
+ * as the required number of data blocks would have
+ * been obtained in the above while loop.
+ */
+ Assert(cstate->raw_buf_index <= DATA_BLOCK_SIZE);
+ }
+ cstate->cur_attname = NULL;
+ }
+}
+
+/*
+ * CopyReadBinaryTupleWorker - Each worker reads data from data blocks after
+ * getting leader-identified tuple offsets from ring data structure.
+ */
+static bool
+CopyReadBinaryTupleWorker(CopyState cstate, Datum *values, bool *nulls)
+{
+ uint32 line_pos;
+ ParallelCopyLineBoundary *line_info;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+ int16 fld_count;
+ ListCell *cur;
+ FmgrInfo *in_functions = cstate->in_functions;
+ Oid *typioparams = cstate->typioparams;
+ TupleDesc tup_desc = RelationGetDescr(cstate->rel);
+
+ line_pos = GetLinePosition(cstate);
+
+ if (line_pos == -1)
+ return true;
+
+ line_info = &pcshared_info->line_boundaries.ring[line_pos];
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[line_info->first_block];
+ cstate->raw_buf_index = line_info->start_offset;
+
+ if (cstate->raw_buf_index + sizeof(fld_count) >= DATA_BLOCK_SIZE)
+ {
+ /*
+ * The case where field count spread across datablocks should never occur,
+ * as the leader would have moved it to next block. this code exists for
+ * debugging purposes only.
+ */
+ elog(DEBUG1, "WORKER - field count spread across datablocks should never occur");
+ }
+
+ memcpy(&fld_count, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_count));
+ fld_count = (int16) pg_ntoh16(fld_count);
+
+ CHECK_FIELD_COUNT;
+
+ cstate->raw_buf_index += sizeof(fld_count);
+
+ foreach(cur, cstate->attnumlist)
+ {
+ int att_num = lfirst_int(cur);
+ int m = att_num - 1;
+ Form_pg_attribute att = TupleDescAttr(tup_desc, m);
+ cstate->cur_attname = NameStr(att->attname);
+
+ values[m] = CopyReadBinaryAttributeWorker(cstate,
+ &in_functions[m],
+ typioparams[m],
+ att->atttypmod,
+ &nulls[m]);
+ cstate->cur_attname = NULL;
+ }
+
+ pg_atomic_sub_fetch_u32(&cstate->pcdata->curr_data_block->unprocessed_line_parts, 1);
+ line_info->start_offset = -1;
+ pg_atomic_write_u32(&line_info->line_state, LINE_WORKER_PROCESSED);
+ pg_atomic_write_u32(&line_info->line_size, -1);
+ pg_atomic_add_fetch_u64(&pcshared_info->total_worker_processed, 1);
+
+ return false;
+}
+
+/*
+ * CopyReadBinaryAttributeWorker - Leader identifies boundaries/offsets
+ * for each attribute/column, it moves on to next data block if the
+ * attribute/column is spread across data blocks.
+ */
+static Datum
+CopyReadBinaryAttributeWorker(CopyState cstate, FmgrInfo *flinfo,
+ Oid typioparam, int32 typmod, bool *isnull)
+{
+ int32 fld_size;
+ Datum result;
+ ParallelCopyShmInfo *pcshared_info = cstate->pcdata->pcshared_info;
+
+ if ((cstate->raw_buf_index + sizeof(fld_size)) >= DATA_BLOCK_SIZE)
+ {
+ ParallelCopyDataBlock *prev_data_block = cstate->pcdata->curr_data_block;
+ elog(DEBUG1, "WORKER - field size is spread across data blocks");
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[prev_data_block->following_block];
+ pg_atomic_sub_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ cstate->raw_buf_index = 0;
+ }
+
+ memcpy(&fld_size, &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], sizeof(fld_size));
+ fld_size = (int32) pg_ntoh32(fld_size);
+
+ CHECK_FIELD_SIZE(fld_size);
+
+ cstate->raw_buf_index += sizeof(fld_size);
+
+ /* reset attribute_buf to empty, and load raw data in it */
+ resetStringInfo(&cstate->attribute_buf);
+
+ enlargeStringInfo(&cstate->attribute_buf, fld_size);
+
+ if ((DATA_BLOCK_SIZE - cstate->raw_buf_index) >= fld_size)
+ {
+ elog(DEBUG1, "WORKER - tuple lies in single data block");
+ memcpy(&cstate->attribute_buf.data[0],&cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], fld_size);
+ cstate->raw_buf_index += fld_size;
+ }
+ else
+ {
+ uint32 att_buf_idx = 0;
+ uint32 copy_bytes = 0;
+ int32 required_blks = 0;
+ int32 curr_blk_bytes = (DATA_BLOCK_SIZE - cstate->raw_buf_index);
+ ParallelCopyDataBlock *prev_data_block = NULL;
+ int i = 0;
+
+ GET_REQUIRED_BLOCKS(required_blks, fld_size, curr_blk_bytes);
+
+ i = required_blks;
+ prev_data_block = cstate->pcdata->curr_data_block;
+ elog(DEBUG1, "WORKER - tuple is spread across data blocks");
+ memcpy(&cstate->attribute_buf.data[0], &prev_data_block->data[cstate->raw_buf_index],
+ curr_blk_bytes);
+ copy_bytes = curr_blk_bytes;
+ att_buf_idx = curr_blk_bytes;
+
+ while (i>0)
+ {
+ cstate->pcdata->curr_data_block = &pcshared_info->data_blocks[prev_data_block->following_block];
+ pg_atomic_sub_fetch_u32(&prev_data_block->unprocessed_line_parts, 1);
+ cstate->raw_buf_index = 0;
+ copy_bytes = fld_size - att_buf_idx;
+
+ /*
+ * The bytes that are yet to be taken into att buff are more than
+ * the entire data block size, but only take the data block size
+ * elements.
+ */
+ if (copy_bytes >= DATA_BLOCK_SIZE)
+ copy_bytes = DATA_BLOCK_SIZE;
+
+ memcpy(&cstate->attribute_buf.data[att_buf_idx],
+ &cstate->pcdata->curr_data_block->data[cstate->raw_buf_index], copy_bytes);
+ att_buf_idx += copy_bytes;
+ i--;
+ }
+ GET_RAW_BUF_INDEX(cstate->raw_buf_index, fld_size, required_blks, curr_blk_bytes);
+ }
+
+ cstate->attribute_buf.len = fld_size;
+ cstate->attribute_buf.data[fld_size] = '\0';
+
+ /* Call the column type's binary input converter */
+ result = ReceiveFunctionCall(flinfo, &cstate->attribute_buf,
+ typioparam, typmod);
+
+ /* Trouble if it didn't eat the whole buffer */
+ if (cstate->attribute_buf.cursor != cstate->attribute_buf.len)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("incorrect binary data format")));
+
+ *isnull = false;
+ return result;
+}
+
+/*
* GetLinePosition - Return the line position that worker should process.
*/
static uint32
@@ -1675,7 +2182,9 @@ GetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
{
dataBlkPtr->curr_blk_completed = false;
dataBlkPtr->skip_bytes = 0;
+ dataBlkPtr->following_block = -1;
pcshared_info->cur_block_pos = block_pos;
+ MemSet(&dataBlkPtr->data[0], 0, DATA_BLOCK_SIZE);
return block_pos;
}
@@ -2191,10 +2700,26 @@ CopyGetInt32(CopyState cstate, int32 *val)
{
uint32 buf;
- if (CopyReadBinaryData(cstate, (char *) &buf, sizeof(buf)) != sizeof(buf))
+ /*
+ * For parallel copy, avoid reading data to raw buf, read directly
+ * from file, later the data will be read to parallel copy data
+ * buffers.
+ */
+ if (cstate->nworkers > 0)
{
- *val = 0; /* suppress compiler warning */
- return false;
+ if (CopyGetData(cstate, &buf, sizeof(buf), sizeof(buf)) != sizeof(buf))
+ {
+ *val = 0; /* suppress compiler warning */
+ return false;
+ }
+ }
+ else
+ {
+ if (CopyReadBinaryData(cstate, (char *) &buf, sizeof(buf)) != sizeof(buf))
+ {
+ *val = 0; /* suppress compiler warning */
+ return false;
+ }
}
*val = (int32) pg_ntoh32(buf);
return true;
@@ -2583,7 +3108,15 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
EndParallelCopy(pcxt);
}
else
+ {
+ /*
+ * Reset nworkers to -1 here. This is useful in cases where user
+ * specifies parallel workers, but, no worker is picked up, so go
+ * back to non parallel mode value of nworkers.
+ */
+ cstate->nworkers = -1;
*processed = CopyFrom(cstate); /* copy from file to database */
+ }
EndCopyFrom(cstate);
}
@@ -5047,7 +5580,7 @@ BeginCopyFrom(ParseState *pstate,
* only in text mode.
*/
initStringInfo(&cstate->attribute_buf);
- cstate->raw_buf = (IsParallelCopy()) ? NULL : (char *) palloc(RAW_BUF_SIZE + 1);
+ cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
cstate->raw_buf_index = cstate->raw_buf_len = 0;
if (!cstate->binary)
{
@@ -5127,7 +5660,7 @@ BeginCopyFrom(ParseState *pstate,
int32 tmp;
/* Signature */
- if (CopyReadBinaryData(cstate, readSig, 11) != 11 ||
+ if (CopyGetData(cstate, readSig, 11, 11) != 11 ||
memcmp(readSig, BinarySignature, 11) != 0)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
@@ -5155,7 +5688,7 @@ BeginCopyFrom(ParseState *pstate,
/* Skip extension header, if present */
while (tmp-- > 0)
{
- if (CopyReadBinaryData(cstate, readSig, 1) != 1)
+ if (CopyGetData(cstate, readSig, 1, 1) != 1)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("invalid COPY file header (wrong length)")));
@@ -5352,60 +5885,45 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext,
else
{
/* binary */
- int16 fld_count;
- ListCell *cur;
-
cstate->cur_lineno++;
+ cstate->max_fields = list_length(cstate->attnumlist);
- if (!CopyGetInt16(cstate, &fld_count))
+ if (!IsParallelCopy())
{
- /* EOF detected (end of file, or protocol-level EOF) */
- return false;
- }
+ int16 fld_count;
+ ListCell *cur;
- if (fld_count == -1)
- {
- /*
- * Received EOF marker. In a V3-protocol copy, wait for the
- * protocol-level EOF, and complain if it doesn't come
- * immediately. This ensures that we correctly handle CopyFail,
- * if client chooses to send that now.
- *
- * Note that we MUST NOT try to read more data in an old-protocol
- * copy, since there is no protocol-level EOF marker then. We
- * could go either way for copy from file, but choose to throw
- * error if there's data after the EOF marker, for consistency
- * with the new-protocol case.
- */
- char dummy;
-
- if (cstate->copy_dest != COPY_OLD_FE &&
- CopyReadBinaryData(cstate, &dummy, 1) > 0)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("received copy data after EOF marker")));
- return false;
- }
+ if (!CopyGetInt16(cstate, &fld_count))
+ {
+ /* EOF detected (end of file, or protocol-level EOF) */
+ return false;
+ }
- if (fld_count != attr_count)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("row field count is %d, expected %d",
- (int) fld_count, attr_count)));
+ CHECK_FIELD_COUNT;
- foreach(cur, cstate->attnumlist)
+ foreach(cur, cstate->attnumlist)
+ {
+ int attnum = lfirst_int(cur);
+ int m = attnum - 1;
+ Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+
+ cstate->cur_attname = NameStr(att->attname);
+ values[m] = CopyReadBinaryAttribute(cstate,
+ &in_functions[m],
+ typioparams[m],
+ att->atttypmod,
+ &nulls[m]);
+ cstate->cur_attname = NULL;
+ }
+ }
+ else
{
- int attnum = lfirst_int(cur);
- int m = attnum - 1;
- Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+ bool eof = false;
- cstate->cur_attname = NameStr(att->attname);
- values[m] = CopyReadBinaryAttribute(cstate,
- &in_functions[m],
- typioparams[m],
- att->atttypmod,
- &nulls[m]);
- cstate->cur_attname = NULL;
+ eof = CopyReadBinaryTupleWorker(cstate, values, nulls);
+
+ if (eof)
+ return false;
}
}
@@ -6405,18 +6923,15 @@ CopyReadBinaryAttribute(CopyState cstate, FmgrInfo *flinfo,
Datum result;
if (!CopyGetInt32(cstate, &fld_size))
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("unexpected EOF in COPY data")));
+ EOF_ERROR;
+
if (fld_size == -1)
{
*isnull = true;
return ReceiveFunctionCall(flinfo, NULL, typioparam, typmod);
}
- if (fld_size < 0)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("invalid field size")));
+
+ CHECK_FIELD_SIZE(fld_size);
/* reset attribute_buf to empty, and load raw data in it */
resetStringInfo(&cstate->attribute_buf);
@@ -6424,9 +6939,7 @@ CopyReadBinaryAttribute(CopyState cstate, FmgrInfo *flinfo,
enlargeStringInfo(&cstate->attribute_buf, fld_size);
if (CopyReadBinaryData(cstate, cstate->attribute_buf.data,
fld_size) != fld_size)
- ereport(ERROR,
- (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
- errmsg("unexpected EOF in COPY data")));
+ EOF_ERROR;
cstate->attribute_buf.len = fld_size;
cstate->attribute_buf.data[fld_size] = '\0';
--
1.8.3.1
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-22 10:38 Bharath Rupireddy <[email protected]>
parent: Bharath Rupireddy <[email protected]>
0 siblings, 2 replies; 2431+ messages in thread
From: Bharath Rupireddy @ 2020-09-22 10:38 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: vignesh C <[email protected]>; PostgreSQL Hackers <[email protected]>
On Thu, Sep 17, 2020 at 11:06 AM Bharath Rupireddy <
[email protected]> wrote:
>
> On Wed, Sep 16, 2020 at 1:20 PM Greg Nancarrow <[email protected]>
wrote:
> >
> > Fortunately I have been given permission to share the exact table
> > definition and data I used, so you can check the behaviour and timings
> > on your own test machine.
> >
>
> Thanks Greg for the script. I ran your test case and I didn't observe
> any increase in exec time with 1 worker, indeed, we have benefitted a
> few seconds from 0 to 1 worker as expected.
>
> Execution time is in seconds. Each test case is executed 3 times on
> release build. Each time the data directory is recreated.
>
> Case 1: 1000000 rows, 2GB
> With Patch, default configuration, 0 worker: 88.933, 92.261, 88.423
> With Patch, default configuration, 1 worker: 73.825, 74.583, 72.678
>
> With Patch, custom configuration, 0 worker: 76.191, 78.160, 78.822
> With Patch, custom configuration, 1 worker: 61.289, 61.288, 60.573
>
> Case 2: 2550000 rows, 5GB
> With Patch, default configuration, 0 worker: 246.031, 188.323, 216.683
> With Patch, default configuration, 1 worker: 156.299, 153.293, 170.307
>
> With Patch, custom configuration, 0 worker: 197.234, 195.866, 196.049
> With Patch, custom configuration, 1 worker: 157.173, 158.287, 157.090
>
Hi Greg,
If you still observe the issue in your testing environment, I'm attaching a
testing patch(that applies on top of the latest parallel copy patch set
i.e. v5 1 to 6) to capture various timings such as total copy time in
leader and worker, index and table insertion time, leader and worker
waiting time. These logs are shown in the server log file.
Few things to follow before testing:
1. Is the table being dropped/truncated after the test with 0 workers and
before running with 1 worker? If not, then the index insertion time would
increase.[1](for me it is increasing by 10 sec). This is obvious because
the 1st time index will be created from bottom up manner(from leaves to
root), but for the 2nd time it has to search and insert at the proper
leaves and inner B+Tree nodes.
2. If possible, can you also run with custom postgresql.conf settings[2]
along with default? Just to ensure that other bg processes such as
checkpointer, autovacuum, bgwriter etc. don't affect our testcase. For
instance, with default postgresql.conf file, it looks like checkpointing[3]
is happening frequently, could you please let us know if that happens at
your end?
3. Could you please run the test case 3 times at least? Just to ensure the
consistency of the issue.
4. I ran the tests in a performance test system where no other user
processes(except system processes) are running. Is it possible for you to
do the same?
Please capture and share the timing logs with us.
Here's a snapshot of how the added timings show up in the logs: ( I
captured this with your test case case 1: 1000000 rows, 2GB, custom
postgresql.conf file settings[2]).
with 0 workers:
2020-09-22 10:49:27.508 BST [163910] LOG: totaltableinsertiontime =
24072.034 ms
2020-09-22 10:49:27.508 BST [163910] LOG: totalindexinsertiontime = 60.682
ms
2020-09-22 10:49:27.508 BST [163910] LOG: totalcopytime = 59664.594 ms
with 1 worker:
2020-09-22 10:53:58.409 BST [163947] LOG: totalcopyworkerwaitingtime =
59.815 ms
2020-09-22 10:53:58.409 BST [163947] LOG: totaltableinsertiontime =
23585.881 ms
2020-09-22 10:53:58.409 BST [163947] LOG: totalindexinsertiontime = 30.946
ms
2020-09-22 10:53:58.409 BST [163947] LOG: totalcopytimeworker = 47047.956
ms
2020-09-22 10:53:58.429 BST [163946] LOG: totalcopyleaderwaitingtime =
26746.744 ms
2020-09-22 10:53:58.429 BST [163946] LOG: totalcopytime = 47150.002 ms
[1]
0 worker:
LOG: totaltableinsertiontime = 25491.881 ms
LOG: totalindexinsertiontime = 14136.104 ms
LOG: totalcopytime = 75606.858 ms
table is not dropped and so are indexes
1 worker:
LOG: totalcopyworkerwaitingtime = 64.582 ms
LOG: totaltableinsertiontime = 21360.875 ms
LOG: totalindexinsertiontime = 24843.570 ms
LOG: totalcopytimeworker = 69837.162 ms
LOG: totalcopyleaderwaitingtime = 49548.441 ms
LOG: totalcopytime = 69997.778 ms
[2]
custom postgresql.conf configuration:
shared_buffers = 40GB
max_worker_processes = 32
max_parallel_maintenance_workers = 24
max_parallel_workers = 32
synchronous_commit = off
checkpoint_timeout = 1d
max_wal_size = 24GB
min_wal_size = 15GB
autovacuum = off
[3]
LOG: checkpoints are occurring too frequently (14 seconds apart)
HINT: Consider increasing the configuration parameter "max_wal_size".
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
Attachments:
[application/octet-stream] v1-0001-Parallel-Copy-Exec-Time-Capture.patch (7.3K, ../../CALj2ACXQPiPhqShCgdPM3_PZqydxPHvefg3E1N5kNBh2jD08mQ@mail.gmail.com/3-v1-0001-Parallel-Copy-Exec-Time-Capture.patch)
download | inline diff:
From 28c5b37c2271b623f6bc4653d17f92dedb8722be Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <[email protected]>
Date: Tue, 22 Sep 2020 15:12:27 +0530
Subject: [PATCH v2] Parallel Copy Exec Time Capture
A testing patch for capturing various timings such as total copy
time in leader and worker, index insertion time, leader and worker
waiting time.
---
src/backend/commands/copy.c | 74 ++++++++++++++++++++++++++++++++++++-
1 file changed, 73 insertions(+), 1 deletion(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 5b1884acd8..cb72949e0e 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -65,6 +65,14 @@
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#define OCTVALUE(c) ((c) - '0')
+/* Global variables for capturing parallel copy execution times. */
+double totalcopytime;
+double totalcopytimeworker;
+double totalcopyleaderwaitingtime;
+double totalcopyworkerwaitingtime;
+double totaltableinsertiontime;
+double totalindexinsertiontime;
+
/*
* Represents the different source/dest cases we need to worry about at
* the bottom level
@@ -1332,9 +1340,16 @@ CacheLineInfo(CopyState cstate, uint32 buff_count)
uint32 offset;
int dataSize;
int copiedSize = 0;
+ struct timespec before, after;
+ struct timespec before1, after1;
resetStringInfo(&pcdata->worker_line_buf[buff_count].line_buf);
+ INSTR_TIME_SET_CURRENT(before);
write_pos = GetLinePosition(cstate);
+ INSTR_TIME_SET_CURRENT(after);
+ INSTR_TIME_SUBTRACT(after, before);
+ totalcopyworkerwaitingtime += INSTR_TIME_GET_MILLISEC(after);
+
if (-1 == write_pos)
return true;
@@ -1436,6 +1451,7 @@ CacheLineInfo(CopyState cstate, uint32 buff_count)
data_blk_ptr = &pcshared_info->data_blocks[data_blk_ptr->following_block];
}
+ INSTR_TIME_SET_CURRENT(before1);
for (;;)
{
/* Get the size of this line */
@@ -1455,6 +1471,9 @@ CacheLineInfo(CopyState cstate, uint32 buff_count)
COPY_WAIT_TO_PROCESS()
}
+ INSTR_TIME_SET_CURRENT(after1);
+ INSTR_TIME_SUBTRACT(after1, before1);
+ totalcopyworkerwaitingtime += INSTR_TIME_GET_MILLISEC(after1);
}
empty_data_line_update:
@@ -1538,6 +1557,11 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
char *convertListStr = NULL;
WalUsage *walusage;
BufferUsage *bufferusage;
+ struct timespec before, after;
+ totalcopytimeworker = 0;
+ totalcopyworkerwaitingtime = 0;
+ totaltableinsertiontime = 0;
+ totalindexinsertiontime = 0;
/* Allocate workspace and zero all fields. */
cstate = (CopyStateData *) palloc0(sizeof(CopyStateData));
@@ -1606,7 +1630,15 @@ ParallelCopyMain(dsm_segment *seg, shm_toc *toc)
cstate->rel = rel;
InitializeParallelCopyInfo(shared_cstate, cstate, attlist);
+ INSTR_TIME_SET_CURRENT(before);
CopyFrom(cstate);
+ INSTR_TIME_SET_CURRENT(after);
+ INSTR_TIME_SUBTRACT(after, before);
+ totalcopytimeworker += INSTR_TIME_GET_MILLISEC(after);
+ ereport(LOG, (errmsg("totalcopyworkerwaitingtime = %.3f ms", totalcopyworkerwaitingtime), errhidestmt(true)));
+ ereport(LOG, (errmsg("totaltableinsertiontime = %.3f ms", totaltableinsertiontime), errhidestmt(true)));
+ ereport(LOG, (errmsg("totalindexinsertiontime = %.3f ms", totalindexinsertiontime), errhidestmt(true)));
+ ereport(LOG, (errmsg("totalcopytimeworker = %.3f ms", totalcopytimeworker), errhidestmt(true)));
if (rel != NULL)
table_close(rel, RowExclusiveLock);
@@ -1633,11 +1665,16 @@ UpdateBlockInLineInfo(CopyState cstate, uint32 blk_pos,
ParallelCopyLineBoundaries *lineBoundaryPtr = &pcshared_info->line_boundaries;
ParallelCopyLineBoundary *lineInfo;
int line_pos = lineBoundaryPtr->pos;
+ struct timespec before, after;
/* Update the line information for the worker to pick and process. */
lineInfo = &lineBoundaryPtr->ring[line_pos];
+ INSTR_TIME_SET_CURRENT(before);
while (pg_atomic_read_u32(&lineInfo->line_size) != -1)
COPY_WAIT_TO_PROCESS()
+ INSTR_TIME_SET_CURRENT(after);
+ INSTR_TIME_SUBTRACT(after, before);
+ totalcopyleaderwaitingtime += INSTR_TIME_GET_MILLISEC(after);
lineInfo->first_block = blk_pos;
lineInfo->start_offset = offset;
@@ -2203,6 +2240,8 @@ static uint32
WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
{
uint32 new_free_pos = -1;
+ struct timespec before, after;
+ INSTR_TIME_SET_CURRENT(before);
for (;;)
{
new_free_pos = GetFreeCopyBlock(pcshared_info);
@@ -2211,7 +2250,9 @@ WaitGetFreeCopyBlock(ParallelCopyShmInfo *pcshared_info)
COPY_WAIT_TO_PROCESS()
}
-
+ INSTR_TIME_SET_CURRENT(after);
+ INSTR_TIME_SUBTRACT(after, before);
+ totalcopyleaderwaitingtime += INSTR_TIME_GET_MILLISEC(after);
return new_free_pos;
}
@@ -3083,12 +3124,21 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
if (is_from)
{
ParallelContext *pcxt = NULL;
+ struct timespec before, after;
Assert(rel);
+ totalcopytime = 0;
+ totalcopytimeworker = 0;
+ totalcopyleaderwaitingtime = 0;
+ totalcopyworkerwaitingtime = 0;
+ totaltableinsertiontime = 0;
+ totalindexinsertiontime = 0;
/* check read-only transaction and parallel mode */
if (XactReadOnly && !rel->rd_islocaltemp)
PreventCommandIfReadOnly("COPY FROM");
+ INSTR_TIME_SET_CURRENT(before);
+
cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
NULL, stmt->attlist, stmt->options);
cstate->whereClause = whereClause;
@@ -3119,6 +3169,18 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
}
EndCopyFrom(cstate);
+
+ INSTR_TIME_SET_CURRENT(after);
+ INSTR_TIME_SUBTRACT(after, before);
+ totalcopytime += INSTR_TIME_GET_MILLISEC(after);
+ if (pcxt != NULL)
+ ereport(LOG, (errmsg("totalcopyleaderwaitingtime = %.3f ms", totalcopyleaderwaitingtime), errhidestmt(true)));
+ if (pcxt == NULL)
+ {
+ ereport(LOG, (errmsg("totaltableinsertiontime = %.3f ms", totaltableinsertiontime), errhidestmt(true)));
+ ereport(LOG, (errmsg("totalindexinsertiontime = %.3f ms", totalindexinsertiontime), errhidestmt(true)));
+ }
+ ereport(LOG, (errmsg("totalcopytime = %.3f ms", totalcopytime), errhidestmt(true)));
}
else
{
@@ -4527,6 +4589,8 @@ CopyMultiInsertBufferFlush(CopyMultiInsertInfo *miinfo,
int nused = buffer->nused;
ResultRelInfo *resultRelInfo = buffer->resultRelInfo;
TupleTableSlot **slots = buffer->slots;
+ struct timespec before, after;
+ struct timespec before1, after1;
/* Set es_result_relation_info to the ResultRelInfo we're flushing. */
estate->es_result_relation_info = resultRelInfo;
@@ -4543,14 +4607,19 @@ CopyMultiInsertBufferFlush(CopyMultiInsertInfo *miinfo,
* context before calling it.
*/
oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
+ INSTR_TIME_SET_CURRENT(before);
table_multi_insert(resultRelInfo->ri_RelationDesc,
slots,
nused,
mycid,
ti_options,
buffer->bistate);
+ INSTR_TIME_SET_CURRENT(after);
+ INSTR_TIME_SUBTRACT(after, before);
+ totaltableinsertiontime += INSTR_TIME_GET_MILLISEC(after);
MemoryContextSwitchTo(oldcontext);
+ INSTR_TIME_SET_CURRENT(before1);
for (i = 0; i < nused; i++)
{
/*
@@ -4586,6 +4655,9 @@ CopyMultiInsertBufferFlush(CopyMultiInsertInfo *miinfo,
ExecClearTuple(slots[i]);
}
+ INSTR_TIME_SET_CURRENT(after1);
+ INSTR_TIME_SUBTRACT(after1, before1);
+ totalindexinsertiontime += INSTR_TIME_GET_MILLISEC(after1);
/* Mark that all slots are free */
buffer->nused = 0;
--
2.25.1
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-24 02:56 Greg Nancarrow <[email protected]>
parent: Bharath Rupireddy <[email protected]>
1 sibling, 1 reply; 2431+ messages in thread
From: Greg Nancarrow @ 2020-09-24 02:56 UTC (permalink / raw)
To: Bharath Rupireddy <[email protected]>; +Cc: vignesh C <[email protected]>; PostgreSQL Hackers <[email protected]>
Hi Bharath,
> Few things to follow before testing:
> 1. Is the table being dropped/truncated after the test with 0 workers and before running with 1 worker? If not, then the index insertion time would increase.[1](for me it is increasing by 10 sec). This is obvious because the 1st time index will be created from bottom up manner(from leaves to root), but for the 2nd time it has to search and insert at the proper leaves and inner B+Tree nodes.
Yes, it' being truncated before running each and every COPY.
> 2. If possible, can you also run with custom postgresql.conf settings[2] along with default? Just to ensure that other bg processes such as checkpointer, autovacuum, bgwriter etc. don't affect our testcase. For instance, with default postgresql.conf file, it looks like checkpointing[3] is happening frequently, could you please let us know if that happens at your end?
Yes, have run with default and your custom settings. With default
settings, I can confirm that checkpointing is happening frequently
with the tests I've run here.
> 3. Could you please run the test case 3 times at least? Just to ensure the consistency of the issue.
Yes, have run 4 times. Seems to be a performance hit (whether normal
copy or parallel-1 copy) on the first COPY run on a freshly created
database. After that, results are consistent.
> 4. I ran the tests in a performance test system where no other user processes(except system processes) are running. Is it possible for you to do the same?
>
> Please capture and share the timing logs with us.
>
Yes, I have ensured the system is as idle as possible prior to testing.
I have attached the test results obtained after building with your
Parallel Copy patch and testing patch applied (HEAD at
733fa9aa51c526582f100aa0d375e0eb9a6bce8b).
Test results show that Parallel COPY with 1 worker is performing
better than normal COPY in the test scenarios run. There is a
performance hit (regardless of COPY type) on the very first COPY run
on a freshly-created database.
I ran the test case 4 times. and also in reverse order, with truncate
run before each COPY (output and logs named xxxx_0_1 run normal COPY
then parallel COPY, and named xxxx_1_0 run parallel COPY and then
normal COPY).
Please refer to attached results.
Regards,
Greg
Attachments:
[application/gzip] testing_patch_results.tar.gz (5.2K, ../../CAJcOf-dzYj9-8Fb9aLebi3BCq7sHnKHDAUcN0nG-MLromDC2DA@mail.gmail.com/2-testing_patch_results.tar.gz)
download
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-24 07:04 Bharath Rupireddy <[email protected]>
parent: Greg Nancarrow <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bharath Rupireddy @ 2020-09-24 07:04 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: vignesh C <[email protected]>; PostgreSQL Hackers <[email protected]>
--000000000000220eb605b009d153
Content-Type: text/plain; charset="UTF-8"
Thanks Greg for the testing.
On Thu, Sep 24, 2020 at 8:27 AM Greg Nancarrow <[email protected]> wrote:
>
> > 3. Could you please run the test case 3 times at least? Just to ensure
the consistency of the issue.
>
> Yes, have run 4 times. Seems to be a performance hit (whether normal
> copy or parallel-1 copy) on the first COPY run on a freshly created
> database. After that, results are consistent.
>
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-24 09:30 Bharath Rupireddy <[email protected]>
parent: vignesh C <[email protected]>
2 siblings, 1 reply; 2431+ messages in thread
From: Bharath Rupireddy @ 2020-09-24 09:30 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: Ashutosh Sharma <[email protected]>; Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>
>
> > Have you tested your patch when encoding conversion is needed? If so,
> > could you please point out the email that has the test results.
> >
>
> We have not yet done encoding testing, we will do and post the results
> separately in the coming days.
>
Hi Ashutosh,
I ran the tests ensuring pg_server_to_any() gets called from copy.c. I
specified the encoding option of COPY command, with client and server
encodings being UTF-8.
Tests are performed with custom postgresql.conf[1], 10million rows, 5.2GB
data. The results are of the triplet form (exec time in sec, number of
workers, gain)
Use case 1: 2 indexes on integer columns, 1 index on text column
(1174.395, 0, 1X), (1127.792, 1, 1.04X), (644.260, 2, 1.82X), (341.284, 4,
3.43X), (204.423, 8, 5.74X), (140.692, 16, 8.34X), (129.843, 20, 9.04X),
(134.511, 30, 8.72X)
Use case 2: 1 gist index on text column
(811.412, 0, 1X), (772.203, 1, 1.05X), (437.364, 2, 1.85X), (263.575, 4,
3.08X), (175.135, 8, 4.63X), (155.355, 16, 5.22X), (178.704, 20, 4.54X),
(199.402, 30, 4.06)
Use case 3: 3 indexes on integer columns
(220.680, 0, 1X), (185.096, 1, 1.19X), (134.811, 2, 1.64X), (114.585, 4,
1.92X), (107.707, 8, 2.05X), (101.253, 16, 2.18X), (100.749, 20, 2.19X),
(100.656, 30, 2.19X)
The results are similar to our earlier runs[2].
[1]
shared_buffers = 40GB
max_worker_processes = 32
max_parallel_maintenance_workers = 24
max_parallel_workers = 32
synchronous_commit = off
checkpoint_timeout = 1d
max_wal_size = 24GB
min_wal_size = 15GB
autovacuum = off
[2]
https://www.postgresql.org/message-id/CALDaNm13zK%3DJXfZWqZJsm3%2B2yagYDJc%3DeJBgE4i77-4PPNj7vw%40ma...
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-24 13:38 Ashutosh Sharma <[email protected]>
parent: Bharath Rupireddy <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Ashutosh Sharma @ 2020-09-24 13:38 UTC (permalink / raw)
To: Bharath Rupireddy <[email protected]>; +Cc: vignesh C <[email protected]>; Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>
On Thu, Sep 24, 2020 at 3:00 PM Bharath Rupireddy
<[email protected]> wrote:
>
> >
> > > Have you tested your patch when encoding conversion is needed? If so,
> > > could you please point out the email that has the test results.
> > >
> >
> > We have not yet done encoding testing, we will do and post the results
> > separately in the coming days.
> >
>
> Hi Ashutosh,
>
> I ran the tests ensuring pg_server_to_any() gets called from copy.c. I specified the encoding option of COPY command, with client and server encodings being UTF-8.
>
Thanks Bharath for the testing. The results look impressive.
--
With Regards,
Ashutosh Sharma
EnterpriseDB:http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-28 09:31 Amit Kapila <[email protected]>
parent: vignesh C <[email protected]>
2 siblings, 2 replies; 2431+ messages in thread
From: Amit Kapila @ 2020-09-28 09:31 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: Ashutosh Sharma <[email protected]>; Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Tue, Sep 22, 2020 at 2:44 PM vignesh C <[email protected]> wrote:
>
> Thanks Ashutosh for your comments.
>
> On Wed, Sep 16, 2020 at 6:36 PM Ashutosh Sharma <[email protected]> wrote:
> >
> > Hi Vignesh,
> >
> > I've spent some time today looking at your new set of patches and I've
> > some thoughts and queries which I would like to put here:
> >
> > Why are these not part of the shared cstate structure?
> >
> > SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
> > SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
> > SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
> > SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
> >
>
> I have used shared_cstate mainly to share the integer & bool data
> types from the leader to worker process. The above data types are of
> char* data type, I will not be able to use it like how I could do it
> for integer type. So I preferred to send these as separate keys to the
> worker. Thoughts?
>
I think the way you have written will work but if we go with
Ashutosh's proposal it will look elegant and in the future, if we need
to share more strings as part of cstate structure then that would be
easier. You can probably refer to EstimateParamListSpace,
SerializeParamList, and RestoreParamList to see how we can share
different types of data in one key.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-28 13:06 Ashutosh Sharma <[email protected]>
parent: Amit Kapila <[email protected]>
1 sibling, 1 reply; 2431+ messages in thread
From: Ashutosh Sharma @ 2020-09-28 13:06 UTC (permalink / raw)
To: Amit Kapila <[email protected]>; +Cc: vignesh C <[email protected]>; Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Mon, Sep 28, 2020 at 3:01 PM Amit Kapila <[email protected]> wrote:
>
> On Tue, Sep 22, 2020 at 2:44 PM vignesh C <[email protected]> wrote:
> >
> > Thanks Ashutosh for your comments.
> >
> > On Wed, Sep 16, 2020 at 6:36 PM Ashutosh Sharma <[email protected]> wrote:
> > >
> > > Hi Vignesh,
> > >
> > > I've spent some time today looking at your new set of patches and I've
> > > some thoughts and queries which I would like to put here:
> > >
> > > Why are these not part of the shared cstate structure?
> > >
> > > SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
> > > SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
> > > SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
> > > SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
> > >
> >
> > I have used shared_cstate mainly to share the integer & bool data
> > types from the leader to worker process. The above data types are of
> > char* data type, I will not be able to use it like how I could do it
> > for integer type. So I preferred to send these as separate keys to the
> > worker. Thoughts?
> >
>
> I think the way you have written will work but if we go with
> Ashutosh's proposal it will look elegant and in the future, if we need
> to share more strings as part of cstate structure then that would be
> easier. You can probably refer to EstimateParamListSpace,
> SerializeParamList, and RestoreParamList to see how we can share
> different types of data in one key.
>
Yeah. And in addition to that it will also reduce the number of DSM
keys that we need to maintain.
--
With Regards,
Ashutosh Sharma
EnterpriseDB:http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-09-29 09:45 Greg Nancarrow <[email protected]>
parent: vignesh C <[email protected]>
2 siblings, 2 replies; 2431+ messages in thread
From: Greg Nancarrow @ 2020-09-29 09:45 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: Ashutosh Sharma <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
Hi Vignesh and Bharath,
Seems like the Parallel Copy patch is regarding RI_TRIGGER_PK as
parallel-unsafe.
Can you explain why this is?
Regards,
Greg Nancarrow
Fujitsu Australia
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-10-01 06:43 Amit Kapila <[email protected]>
parent: Greg Nancarrow <[email protected]>
1 sibling, 0 replies; 2431+ messages in thread
From: Amit Kapila @ 2020-10-01 06:43 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: vignesh C <[email protected]>; Ashutosh Sharma <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Tue, Sep 29, 2020 at 3:16 PM Greg Nancarrow <[email protected]> wrote:
>
> Hi Vignesh and Bharath,
>
> Seems like the Parallel Copy patch is regarding RI_TRIGGER_PK as
> parallel-unsafe.
> Can you explain why this is?
>
I don't think we need to restrict this case and even if there is some
reason to do so then probably the same should be mentioned in the
comments.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-10-03 00:49 Tomas Vondra <[email protected]>
parent: Bharath Rupireddy <[email protected]>
1 sibling, 2 replies; 2431+ messages in thread
From: Tomas Vondra @ 2020-10-03 00:49 UTC (permalink / raw)
To: Bharath Rupireddy <[email protected]>; +Cc: Greg Nancarrow <[email protected]>; vignesh C <[email protected]>; PostgreSQL Hackers <[email protected]>
Hello Vignesh,
I've done some basic benchmarking on the v4 version of the patches (but
AFAIKC the v5 should perform about the same), and some initial review.
For the benchmarking, I used the lineitem table from TPC-H - for 75GB
data set, this largest table is about 64GB once loaded, with another
54GB in 5 indexes. This is on a server with 32 cores, 64GB of RAM and
NVME storage.
The COPY duration with varying number of workers (specified using the
parallel COPY option) looks like this:
workers duration
---------------------
0 1366
1 1255
2 704
3 526
4 434
5 385
6 347
7 322
8 327
So this seems to work pretty well - initially we get almost linear
speedup, then it slows down (likely due to contention for locks, I/O
etc.). Not bad.
I've only done a quick review, but overall the patch looks in fairly
good shape.
1) I don't quite understand why we need INCREMENTPROCESSED and
RETURNPROCESSED, considering it just does ++ or return. It just
obfuscated the code, I think.
2) I find it somewhat strange that BeginParallelCopy can just decide not
to do parallel copy after all. Why not to do this decisions in the
caller? Or maybe it's fine this way, not sure.
3) AFAIK we don't modify typedefs.list in patches, so these changes
should be removed.
4) IsTriggerFunctionParallelSafe actually checks all triggers, not just
one, so the comment needs minor rewording.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-10-03 10:15 Amit Kapila <[email protected]>
parent: Tomas Vondra <[email protected]>
1 sibling, 0 replies; 2431+ messages in thread
From: Amit Kapila @ 2020-10-03 10:15 UTC (permalink / raw)
To: Tomas Vondra <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Greg Nancarrow <[email protected]>; vignesh C <[email protected]>; PostgreSQL Hackers <[email protected]>
On Sat, Oct 3, 2020 at 6:20 AM Tomas Vondra
<[email protected]> wrote:
>
> Hello Vignesh,
>
> I've done some basic benchmarking on the v4 version of the patches (but
> AFAIKC the v5 should perform about the same), and some initial review.
>
> For the benchmarking, I used the lineitem table from TPC-H - for 75GB
> data set, this largest table is about 64GB once loaded, with another
> 54GB in 5 indexes. This is on a server with 32 cores, 64GB of RAM and
> NVME storage.
>
> The COPY duration with varying number of workers (specified using the
> parallel COPY option) looks like this:
>
> workers duration
> ---------------------
> 0 1366
> 1 1255
> 2 704
> 3 526
> 4 434
> 5 385
> 6 347
> 7 322
> 8 327
>
> So this seems to work pretty well - initially we get almost linear
> speedup, then it slows down (likely due to contention for locks, I/O
> etc.). Not bad.
>
+1. These numbers (> 4x speed up) look good to me.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-10-07 18:48 vignesh C <[email protected]>
parent: Greg Nancarrow <[email protected]>
1 sibling, 0 replies; 2431+ messages in thread
From: vignesh C @ 2020-10-07 18:48 UTC (permalink / raw)
To: Greg Nancarrow <[email protected]>; +Cc: Ashutosh Sharma <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Tue, Sep 29, 2020 at 3:16 PM Greg Nancarrow <[email protected]> wrote:
>
> Hi Vignesh and Bharath,
>
> Seems like the Parallel Copy patch is regarding RI_TRIGGER_PK as
> parallel-unsafe.
> Can you explain why this is?
Yes we don't need to restrict parallelism for RI_TRIGGER_PK cases as
we don't do any command counter increments while performing PK checks
as opposed to RI_TRIGGER_FK/foreign key checks. We have modified this
in the v6 patch set.
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-10-07 18:56 vignesh C <[email protected]>
parent: Amit Kapila <[email protected]>
1 sibling, 0 replies; 2431+ messages in thread
From: vignesh C @ 2020-10-07 18:56 UTC (permalink / raw)
To: Amit Kapila <[email protected]>; +Cc: Ashutosh Sharma <[email protected]>; Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Mon, Sep 28, 2020 at 3:01 PM Amit Kapila <[email protected]> wrote:
>
> On Tue, Sep 22, 2020 at 2:44 PM vignesh C <[email protected]> wrote:
> >
> > Thanks Ashutosh for your comments.
> >
> > On Wed, Sep 16, 2020 at 6:36 PM Ashutosh Sharma <[email protected]> wrote:
> > >
> > > Hi Vignesh,
> > >
> > > I've spent some time today looking at your new set of patches and I've
> > > some thoughts and queries which I would like to put here:
> > >
> > > Why are these not part of the shared cstate structure?
> > >
> > > SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
> > > SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
> > > SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
> > > SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
> > >
> >
> > I have used shared_cstate mainly to share the integer & bool data
> > types from the leader to worker process. The above data types are of
> > char* data type, I will not be able to use it like how I could do it
> > for integer type. So I preferred to send these as separate keys to the
> > worker. Thoughts?
> >
>
> I think the way you have written will work but if we go with
> Ashutosh's proposal it will look elegant and in the future, if we need
> to share more strings as part of cstate structure then that would be
> easier. You can probably refer to EstimateParamListSpace,
> SerializeParamList, and RestoreParamList to see how we can share
> different types of data in one key.
>
Thanks for the solution Amit, I have fixed this and handled it in the
v6 patch shared in my previous mail.
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-10-08 05:45 vignesh C <[email protected]>
parent: Ashutosh Sharma <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: vignesh C @ 2020-10-08 05:45 UTC (permalink / raw)
To: Ashutosh Sharma <[email protected]>; +Cc: Amit Kapila <[email protected]>; Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>; Bharath Rupireddy <[email protected]>
On Mon, Sep 28, 2020 at 6:37 PM Ashutosh Sharma <[email protected]> wrote:
>
> On Mon, Sep 28, 2020 at 3:01 PM Amit Kapila <[email protected]> wrote:
> >
> > On Tue, Sep 22, 2020 at 2:44 PM vignesh C <[email protected]> wrote:
> > >
> > > Thanks Ashutosh for your comments.
> > >
> > > On Wed, Sep 16, 2020 at 6:36 PM Ashutosh Sharma <[email protected]> wrote:
> > > >
> > > > Hi Vignesh,
> > > >
> > > > I've spent some time today looking at your new set of patches and I've
> > > > some thoughts and queries which I would like to put here:
> > > >
> > > > Why are these not part of the shared cstate structure?
> > > >
> > > > SerializeString(pcxt, PARALLEL_COPY_KEY_NULL_PRINT, cstate->null_print);
> > > > SerializeString(pcxt, PARALLEL_COPY_KEY_DELIM, cstate->delim);
> > > > SerializeString(pcxt, PARALLEL_COPY_KEY_QUOTE, cstate->quote);
> > > > SerializeString(pcxt, PARALLEL_COPY_KEY_ESCAPE, cstate->escape);
> > > >
> > >
> > > I have used shared_cstate mainly to share the integer & bool data
> > > types from the leader to worker process. The above data types are of
> > > char* data type, I will not be able to use it like how I could do it
> > > for integer type. So I preferred to send these as separate keys to the
> > > worker. Thoughts?
> > >
> >
> > I think the way you have written will work but if we go with
> > Ashutosh's proposal it will look elegant and in the future, if we need
> > to share more strings as part of cstate structure then that would be
> > easier. You can probably refer to EstimateParamListSpace,
> > SerializeParamList, and RestoreParamList to see how we can share
> > different types of data in one key.
> >
>
> Yeah. And in addition to that it will also reduce the number of DSM
> keys that we need to maintain.
>
Thanks Ashutosh, This is handled as part of the v6 patch set.
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Parallel copy
@ 2020-10-14 13:29 vignesh C <[email protected]>
parent: Tomas Vondra <[email protected]>
1 sibling, 0 replies; 2431+ messages in thread
From: vignesh C @ 2020-10-14 13:29 UTC (permalink / raw)
To: Tomas Vondra <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Greg Nancarrow <[email protected]>; PostgreSQL Hackers <[email protected]>
On Sat, Oct 3, 2020 at 6:20 AM Tomas Vondra <[email protected]>
wrote:
>
> Hello Vignesh,
>
> I've done some basic benchmarking on the v4 version of the patches (but
> AFAIKC the v5 should perform about the same), and some initial review.
>
> For the benchmarking, I used the lineitem table from TPC-H - for 75GB
> data set, this largest table is about 64GB once loaded, with another
> 54GB in 5 indexes. This is on a server with 32 cores, 64GB of RAM and
> NVME storage.
>
> The COPY duration with varying number of workers (specified using the
> parallel COPY option) looks like this:
>
> workers duration
> ---------------------
> 0 1366
> 1 1255
> 2 704
> 3 526
> 4 434
> 5 385
> 6 347
> 7 322
> 8 327
>
> So this seems to work pretty well - initially we get almost linear
> speedup, then it slows down (likely due to contention for locks, I/O
> etc.). Not bad.
Thanks for testing with different workers & posting the results.
> I've only done a quick review, but overall the patch looks in fairly
> good shape.
>
> 1) I don't quite understand why we need INCREMENTPROCESSED and
> RETURNPROCESSED, considering it just does ++ or return. It just
> obfuscated the code, I think.
>
I have removed the macros.
> 2) I find it somewhat strange that BeginParallelCopy can just decide not
> to do parallel copy after all. Why not to do this decisions in the
> caller? Or maybe it's fine this way, not sure.
>
I have moved the check IsParallelCopyAllowed to the caller.
> 3) AFAIK we don't modify typedefs.list in patches, so these changes
> should be removed.
>
I had seen that in many of the commits typedefs.list is getting changed,
also it helps in running pgindent. So I'm retaining this change.
> 4) IsTriggerFunctionParallelSafe actually checks all triggers, not just
> one, so the comment needs minor rewording.
>
Modified the comments.
Thanks for the comments & sharing the test results Tomas, These changes are
fixed in one of my earlier mail [1] that I sent.
[1]
https://www.postgresql.org/message-id/CALDaNm1n1xW43neXSGs%3Dc7zt-mj%2BJHHbubWBVDYT9NfCoF8TuQ%40mail...
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-04 05:26 Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 2 replies; 2431+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2024-03-04 05:26 UTC (permalink / raw)
To: '[email protected]' <[email protected]>
Dear hackers,
While reading codes, I found that ApplyLauncherShmemInit() and AutoVacuumShmemInit()
are always called even if they would not be launched.
It may be able to reduce the start time to avoid the unnecessary allocation.
However, I know this improvement would be quite small because the allocated chunks are
quite small.
Anyway, there are several ways to fix:
1)
Skip calling ShmemInitStruct() if the related process would not be launched.
I think this approach is the easiest way. E.g.,
```
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -962,6 +962,9 @@ ApplyLauncherShmemInit(void)
{
bool found;
+ if (max_logical_replication_workers == 0 || IsBinaryUpgrade)
+ return;
+
```
2)
Dynamically allocate the shared memory. This was allowed by recent commit [1].
I made a small PoC only for logical launcher to show what I meant. PSA diff file.
Since some processes (backend, apply worker, parallel apply worker, and tablesync worker)
refers the chunk, codes for attachment must be added on the several places.
If you agree it should be fixed, I will create a patch. Thought?
[1]: https://github.com/postgres/postgres/commit/8b2bcf3f287c79eaebf724cba57e5ff664b01e06
Best Regards,
Hayato Kuroda
FUJITSU LIMITED
https://www.fujitsu.com/
Attachments:
[application/octet-stream] dynamic_allocation.diff (84B, ../../TYCPR01MB12077BFDFBC142086D424FDFEF5232@TYCPR01MB12077.jpnprd01.prod.outlook.com/2-dynamic_allocation.diff)
download | inline diff:
53 22 src/backend/replication/logical/launcher.c
0 1 src/backend/storage/ipc/ipci.c
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-04 05:33 Tom Lane <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
1 sibling, 1 reply; 2431+ messages in thread
From: Tom Lane @ 2024-03-04 05:33 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: '[email protected]' <[email protected]>
"Hayato Kuroda (Fujitsu)" <[email protected]> writes:
> While reading codes, I found that ApplyLauncherShmemInit() and AutoVacuumShmemInit()
> are always called even if they would not be launched.
> It may be able to reduce the start time to avoid the unnecessary allocation.
Why would this be a good idea? It would require preventing the
decision not to launch them from being changed later, except via
postmaster restart. We've generally been trying to move away
from unchangeable-without-restart decisions. In your example,
> + if (max_logical_replication_workers == 0 || IsBinaryUpgrade)
> + return;
max_logical_replication_workers is already PGC_POSTMASTER so there's
not any immediate loss of flexibility, but I don't think it's a great
idea to introduce another reason why it has to be PGC_POSTMASTER.
regards, tom lane
^ permalink raw reply [nested|flat] 2431+ messages in thread
* RE: Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-04 06:10 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Tom Lane <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2024-03-04 06:10 UTC (permalink / raw)
To: 'Tom Lane' <[email protected]>; +Cc: '[email protected]' <[email protected]>
Dear Tom,
Thanks for replying!
> "Hayato Kuroda (Fujitsu)" <[email protected]> writes:
> > While reading codes, I found that ApplyLauncherShmemInit() and
> AutoVacuumShmemInit()
> > are always called even if they would not be launched.
> > It may be able to reduce the start time to avoid the unnecessary allocation.
>
> Why would this be a good idea? It would require preventing the
> decision not to launch them from being changed later, except via
> postmaster restart.
Right. It is important to relax their GucContext.
> We've generally been trying to move away
> from unchangeable-without-restart decisions. In your example,
>
> > + if (max_logical_replication_workers == 0 || IsBinaryUpgrade)
> > + return;
>
> max_logical_replication_workers is already PGC_POSTMASTER so there's
> not any immediate loss of flexibility, but I don't think it's a great
> idea to introduce another reason why it has to be PGC_POSTMASTER.
You are right. The first example implied the max_logical_replication_workers
won't be changed. So it is not appropriate.
So ... what about second one? The approach allows to allocate a memory after
startup, which means that users may able to change the parameter from 0 to
natural number in future. (Of course, such an operation is prohibit for now).
Can it be an initial step to ease the condition?
Best Regards,
Hayato Kuroda
FUJITSU LIMITED
https://www.fujitsu.com/
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-04 08:09 Alvaro Herrera <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
1 sibling, 1 reply; 2431+ messages in thread
From: Alvaro Herrera @ 2024-03-04 08:09 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: '[email protected]' <[email protected]>
On 2024-Mar-04, Hayato Kuroda (Fujitsu) wrote:
> Dear hackers,
>
> While reading codes, I found that ApplyLauncherShmemInit() and
> AutoVacuumShmemInit() are always called even if they would not be
> launched.
Note that there are situations where the autovacuum launcher is started
even though autovacuum is nominally turned off, and I suspect your
proposal would break that. IIRC this occurs when the Xid or multixact
counters cross the max_freeze_age threshold.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"Porque Kim no hacía nada, pero, eso sí,
con extraordinario éxito" ("Kim", Kipling)
^ permalink raw reply [nested|flat] 2431+ messages in thread
* RE: Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-04 09:50 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Alvaro Herrera <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2024-03-04 09:50 UTC (permalink / raw)
To: 'Alvaro Herrera' <[email protected]>; +Cc: '[email protected]' <[email protected]>
Dear Alvaro,
Thanks for giving comments!
> > While reading codes, I found that ApplyLauncherShmemInit() and
> > AutoVacuumShmemInit() are always called even if they would not be
> > launched.
>
> Note that there are situations where the autovacuum launcher is started
> even though autovacuum is nominally turned off, and I suspect your
> proposal would break that. IIRC this occurs when the Xid or multixact
> counters cross the max_freeze_age threshold.
Right. In GetNewTransactionId(), SetTransactionIdLimit() and some other places,
PMSIGNAL_START_AUTOVAC_LAUNCHER is sent to postmaster when the xid exceeds
autovacuum_freeze_max_age. This work has already been written in the doc [1]:
```
To ensure that this does not happen, autovacuum is invoked on any table that
might contain unfrozen rows with XIDs older than the age specified by the
configuration parameter autovacuum_freeze_max_age. (This will happen even
if autovacuum is disabled.)
```
This means that my first idea won't work well. Even if the postmaster does not
initially allocate shared memory, backends may request to start auto vacuum and
use the region. However, the second idea is still valid, which allows the allocation
of shared memory dynamically. This is a bit efficient for the system which tuples
won't be frozen. Thought?
[1]: https://www.postgresql.org/docs/devel/routine-vacuuming.html#VACUUM-FOR-WRAPAROUND
Best Regards,
Hayato Kuroda
FUJITSU LIMITED
https://www.fujitsu.com/
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-04 11:52 'Alvaro Herrera' <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: 'Alvaro Herrera' @ 2024-03-04 11:52 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: '[email protected]' <[email protected]>
On 2024-Mar-04, Hayato Kuroda (Fujitsu) wrote:
> However, the second idea is still valid, which allows the allocation
> of shared memory dynamically. This is a bit efficient for the system
> which tuples won't be frozen. Thought?
I think it would be worth allocating AutoVacuumShmem->av_workItems using
dynamic shmem allocation, particularly to prevent workitems from being
discarded just because the array is full¹; but other than that, the
struct is just 64 bytes long so I doubt it's useful to allocate it
dynamically.
¹ I mean, if the array is full, just allocate another array, point to it
from the original one, and keep going.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"The problem with the facetime model is not just that it's demoralizing, but
that the people pretending to work interrupt the ones actually working."
-- Paul Graham, http://www.paulgraham.com/opensource.html
^ permalink raw reply [nested|flat] 2431+ messages in thread
* RE: Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-04 13:11 Hayato Kuroda (Fujitsu) <[email protected]>
parent: 'Alvaro Herrera' <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2024-03-04 13:11 UTC (permalink / raw)
To: 'Alvaro Herrera' <[email protected]>; +Cc: '[email protected]' <[email protected]>
Dear Alvaro,
Thanks for discussing!
>
> I think it would be worth allocating AutoVacuumShmem->av_workItems using
> dynamic shmem allocation, particularly to prevent workitems from being
> discarded just because the array is full¹; but other than that, the
> struct is just 64 bytes long so I doubt it's useful to allocate it
> dynamically.
>
> ¹ I mean, if the array is full, just allocate another array, point to it
> from the original one, and keep going.
OK, I understood that my initial proposal is not so valuable, so I can withdraw it.
About the suggetion, you imagined AutoVacuumRequestWork() and brininsert(),
right? I agreed it sounds good, but I don't think it can be implemented by current
interface. An interface for dynamically allocating memory is GetNamedDSMSegment(),
and it returns the same shared memory region if input names are the same.
Therefore, there is no way to re-alloc the shared memory.
Best Regards,
Hayato Kuroda
FUJITSU LIMITED
https://www.fujitsu.com/
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-04 13:50 'Alvaro Herrera' <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: 'Alvaro Herrera' @ 2024-03-04 13:50 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: '[email protected]' <[email protected]>
Hello Hayato,
On 2024-Mar-04, Hayato Kuroda (Fujitsu) wrote:
> OK, I understood that my initial proposal is not so valuable, so I can
> withdraw it.
Yeah, that's what it seems to me.
> About the suggetion, you imagined AutoVacuumRequestWork() and
> brininsert(), right?
Correct.
> I agreed it sounds good, but I don't think it can be implemented by
> current interface. An interface for dynamically allocating memory is
> GetNamedDSMSegment(), and it returns the same shared memory region if
> input names are the same. Therefore, there is no way to re-alloc the
> shared memory.
Yeah, I was imagining something like this: the workitem-array becomes a
struct, which has a name and a "next" pointer and a variable number of
workitem slots; the AutoVacuumShmem struct has a pointer to the first
workitem-struct and the last one; when a workitem is requested by
brininsert, we initially allocate via GetNamedDSMSegment("workitem-0") a
workitem-struct with a smallish number of elements; if we request
another workitem and the array is full, we allocate another array via
GetNamedDSMSegment("workitem-1") and store a pointer to it in workitem-0
(so that the list can be followed by an autovacuum worker that's
processing the database), and it's also set as the tail of the list in
AutoVacuumShmem (so that we know where to store further work items).
When all items in a workitem-struct are processed, we can free it
(I guess via dsm_unpin_segment), and make AutoVacuumShmem->av_workitems
point to the next one in the list.
This way, the "array" can grow arbitrarily.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
^ permalink raw reply [nested|flat] 2431+ messages in thread
* RE: Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-05 02:00 Hayato Kuroda (Fujitsu) <[email protected]>
parent: 'Alvaro Herrera' <[email protected]>
0 siblings, 1 reply; 2431+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2024-03-05 02:00 UTC (permalink / raw)
To: 'Alvaro Herrera' <[email protected]>; +Cc: '[email protected]' <[email protected]>
Dear Alvaro,
Thanks for giving comments!
> > I agreed it sounds good, but I don't think it can be implemented by
> > current interface. An interface for dynamically allocating memory is
> > GetNamedDSMSegment(), and it returns the same shared memory region if
> > input names are the same. Therefore, there is no way to re-alloc the
> > shared memory.
>
> Yeah, I was imagining something like this: the workitem-array becomes a
> struct, which has a name and a "next" pointer and a variable number of
> workitem slots; the AutoVacuumShmem struct has a pointer to the first
> workitem-struct and the last one; when a workitem is requested by
> brininsert, we initially allocate via GetNamedDSMSegment("workitem-0") a
> workitem-struct with a smallish number of elements; if we request
> another workitem and the array is full, we allocate another array via
> GetNamedDSMSegment("workitem-1") and store a pointer to it in workitem-0
> (so that the list can be followed by an autovacuum worker that's
> processing the database), and it's also set as the tail of the list in
> AutoVacuumShmem (so that we know where to store further work items).
> When all items in a workitem-struct are processed, we can free it
> (I guess via dsm_unpin_segment), and make AutoVacuumShmem->av_workitems
> point to the next one in the list.
>
> This way, the "array" can grow arbitrarily.
>
Basically sounds good. My concerns are:
* GetNamedDSMSegment() does not returns a raw pointer to dsm_segment. This means
that it may be difficult to do dsm_unpin_segment on the caller side.
* dynamic shared memory is recorded in dhash (dsm_registry_table) and the entry
won't be deleted. The reference for the chunk might be remained.
Overall, it may be needed that dsm_registry may be also extended. I do not start
working yet, so will share results after trying them.
Best Regards,
Hayato Kuroda
FUJITSU LIMITED
https://www.fujitsu.com/
^ permalink raw reply [nested|flat] 2431+ messages in thread
* Re: Some shared memory chunks are allocated even if related processes won't start
@ 2024-03-05 07:34 'Alvaro Herrera' <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: 'Alvaro Herrera' @ 2024-03-05 07:34 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: [email protected]
On 2024-Mar-05, Hayato Kuroda (Fujitsu) wrote:
> Basically sounds good. My concerns are:
>
> * GetNamedDSMSegment() does not returns a raw pointer to dsm_segment. This means
> that it may be difficult to do dsm_unpin_segment on the caller side.
Maybe we don't need a "named" DSM segment at all, and instead just use
bare dsm segments (dsm_create and friends) or a DSA -- not sure. But
see commit 31ae1638ce35, which removed use of a DSA in autovacuum/BRIN.
Maybe fixing this is just a matter of reverting that commit. At the
time, there was a belief that DSA wasn't supported everywhere so we
couldn't use it for autovacuum workitem stuff, but I think our reliance
on DSA is now past the critical point.
BTW, we should turn BRIN autosummarization to be on by default.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"Las mujeres son como hondas: mientras más resistencia tienen,
más lejos puedes llegar con ellas" (Jonas Nightingale, Leap of Faith)
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-22 15:01 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-22 15:01 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
DROP PROPERTY GRAPH to hit the default case and error out with
"unsupported object class".
The bug only manifests when an event trigger is active, because that is what
calls these functions.
This commit adds the missing cases so that DROP PROPERTY GRAPH, DROP PROPERTY GRAPH
IF EXISTS, and DROP SCHEMA CASCADE on schemas containing property graphs all work
correctly when event triggers are present.
It also adds test cases that create an event trigger and then exercise DROP PROPERTY
GRAPH and DROP SCHEMA CASCADE with property graphs.
Author: Bertrand Drouvot <[email protected]>
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 36 +++++++
.../regress/sql/create_property_graph.sql | 36 +++++++
3 files changed, 165 insertions(+)
54.5% src/backend/catalog/
24.2% src/test/regress/expected/
21.1% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..942a1294b15 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -922,5 +922,41 @@ ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
NOTICE: relation "g1" does not exist, skipping
DROP PROPERTY GRAPH IF EXISTS g1;
NOTICE: property graph "g1" does not exist, skipping
+-- Test DROP PROPERTY GRAPH with dependency resolution
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE dpg_t1, dpg_t2;
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table dpg_schema.t
+drop cascades to property graph dpg_schema.g
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..857098dadc8 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -360,6 +360,42 @@ ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error
ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2;
DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- Test DROP PROPERTY GRAPH with dependency resolution
+
+RESET search_path;
+CREATE FUNCTION dpg_evt_func() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+BEGIN END;
+$$;
+
+CREATE EVENT TRIGGER dpg_evt ON ddl_command_end EXECUTE FUNCTION dpg_evt_func();
+
+CREATE TABLE dpg_t1 (id int PRIMARY KEY, val text);
+CREATE TABLE dpg_t2 (id int PRIMARY KEY, src int, dst int);
+CREATE PROPERTY GRAPH dpg_test
+ VERTEX TABLES (dpg_t1 KEY (id) LABEL person PROPERTIES (val AS name))
+ EDGE TABLES (dpg_t2 KEY (id)
+ SOURCE KEY (src) REFERENCES dpg_t1 (id)
+ DESTINATION KEY (dst) REFERENCES dpg_t1 (id)
+ LABEL knows);
+DROP PROPERTY GRAPH dpg_test;
+
+-- table survives graph drop
+SELECT COUNT(*) FROM dpg_t1;
+DROP TABLE dpg_t1, dpg_t2;
+
+-- Test DROP SCHEMA CASCADE with property graphs inside
+CREATE SCHEMA dpg_schema;
+SET search_path = dpg_schema;
+CREATE TABLE t (id int PRIMARY KEY);
+CREATE PROPERTY GRAPH g VERTEX TABLES (t KEY (id));
+RESET search_path;
+DROP SCHEMA dpg_schema CASCADE;
+
+DROP EVENT TRIGGER dpg_evt;
+DROP FUNCTION dpg_evt_func;
+
DROP ROLE regress_graph_user1, regress_graph_user2;
-- leave remaining objects behind for pg_upgrade/pg_dump tests
--
2.34.1
--s/7v445LwpBnK/Gl--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types via the
zero-OID test that exercises pg_identify_object() and pg_identify_object_as_address().
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 ++++++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 4 +
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 2 +
5 files changed, 129 insertions(+)
62.4% src/backend/catalog/
25.6% src/test/regress/expected/
11.8% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..729c3537f25 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -593,7 +593,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +655,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..620dacf1045 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -282,7 +282,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--gtUR827Uq8d5gVsQ--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 2431+ messages in thread
From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)
getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".
During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().
This commit adds the missing cases.
Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.
A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.
Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
src/backend/catalog/objectaddress.c | 93 +++++++++++++++++++
.../expected/create_property_graph.out | 78 ++++++++++++++++
src/test/regress/expected/event_trigger.out | 17 ++++
src/test/regress/expected/object_address.out | 10 +-
.../regress/sql/create_property_graph.sql | 23 +++++
src/test/regress/sql/event_trigger.sql | 13 +++
src/test/regress/sql/object_address.sql | 6 +-
7 files changed, 238 insertions(+), 2 deletions(-)
14.7% src/backend/catalog/
75.5% src/test/regress/expected/
9.7% src/test/regress/sql/
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case PropgraphElementLabelRelationId:
+ appendStringInfoString(&buffer, "property graph element label");
+ break;
+
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
+ case PropgraphLabelPropertyRelationId:
+ appendStringInfoString(&buffer, "property graph label property");
+ break;
+
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphElementLabelRelationId:
+ {
+ Relation ellabelDesc;
+ ScanKeyData skey[1];
+ SysScanDesc ellabelscan;
+ HeapTuple tup;
+ Form_pg_propgraph_element_label pgelform;
+ ObjectAddress oa;
+
+ ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_element_label_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ ellabelscan = systable_beginscan(ellabelDesc,
+ PropgraphElementLabelObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(ellabelscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for element label %u",
+ object->objectId);
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
+ pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(ellabelscan);
+ table_close(ellabelDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphElementRelationId:
{
HeapTuple tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case PropgraphLabelPropertyRelationId:
+ {
+ Relation lblpropDesc;
+ ScanKeyData skey[1];
+ SysScanDesc lblpropscan;
+ HeapTuple tup;
+ Form_pg_propgraph_label_property plpform;
+ ObjectAddress oa;
+
+ lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+ AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_propgraph_label_property_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+ true, NULL, 1, skey);
+
+ tup = systable_getnext(lblpropscan);
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "could not find tuple for label property %u",
+ object->objectId);
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
+ plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+ ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+ appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+ objargs, false));
+
+ systable_endscan(lblpropscan);
+ table_close(lblpropDesc, AccessShareLock);
+ break;
+ }
+
case PropgraphLabelRelationId:
{
HeapTuple tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
type | create_property_graph_tests | g2 | create_property_graph_tests.g2
(21 rows)
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+ obj | ident | addr
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt | ("property graph element",,,"e of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt | ("property graph element label",,,"e of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt | ("property graph label",,,"e of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt | ("property graph label",,,"v1 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt | ("property graph element label",,,"v1 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt | ("property graph label",,,"v2 of create_property_graph_tests.gt") | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt | ("property graph element label",,,"v2 of create_property_graph_tests.gt") | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt | ("property graph property",,,"a of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt | ("property graph property",,,"b of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt | ("property graph property",,,"c of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt | ("property graph property",,,"k1 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt | ("property graph label property",,,"e of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt | ("property graph property",,,"k2 of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt | ("property graph property",,,"m of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt | ("property graph property",,,"n of create_property_graph_tests.gt") | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt | (type,create_property_graph_tests,gt,create_property_graph_tests.gt) | (type,{create_property_graph_tests.gt},{})
+ type gt[] | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[]) | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt | ("property graph element",,,"v1 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt | ("property graph element",,,"v2 of create_property_graph_tests.gt") | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
NOTICE: test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE: test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE: test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING: error for toast table column: unsupported object type "toast table col
WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for property graph element: unsupported object type "property graph element"
+WARNING: error for property graph element label: unrecognized object type "property graph element label"
WARNING: error for property graph label: unsupported object type "property graph label"
+WARNING: error for property graph label property: unrecognized object type "property graph label property"
WARNING: error for property graph property: unsupported object type "property graph property"
-- miscellaneous other errors
select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
-- restore normal output mode
\a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
refobjid = 'create_property_graph_tests.g2'::regclass
ORDER BY 1, 2, 3, 4;
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+ WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+ SELECT classid, objid, objsubid,
+ refclassid, refobjid, refobjsubid
+ FROM pg_depend
+ WHERE refclassid = 'pg_class'::regclass AND
+ refobjid = 'create_property_graph_tests.gt'::regclass
+ UNION ALL
+ SELECT d.classid, d.objid, d.objsubid,
+ d.refclassid, d.refobjid, d.refobjsubid
+ FROM pg_depend d
+ JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+ )
+ SELECT pg_describe_object(classid, objid, objsubid) as obj,
+ pg_identify_object(classid, objid, objsubid) as ident,
+ pg_identify_object_as_address(classid, objid, objsubid) as addr
+ FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
\a\t
SELECT pg_get_propgraphdef('g2'::regclass);
SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (
+ t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+ t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
- ('property graph element'), ('property graph label'), ('property graph property')
+ ('property graph element'), ('property graph element label'),
+ ('property graph label'), ('property graph label property'),
+ ('property graph property')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
('pg_policy'::regclass, 0, 0), -- no policy
('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+ ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+ ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
('pg_publication'::regclass, 0, 0), -- no publication
('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
--
2.34.1
--R84XrR4Hgw0dH3Ec--
^ permalink raw reply [nested|flat] 2431+ messages in thread
end of thread, other threads:[~2026-04-23 09:27 UTC | newest]
Thread overview: 2431+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2020-08-01 04:24 Re: Parallel copy vignesh C <[email protected]>
2020-08-03 07:03 ` Bharath Rupireddy <[email protected]>
2020-08-04 16:21 ` Tomas Vondra <[email protected]>
2020-08-06 04:49 ` vignesh C <[email protected]>
2020-08-12 03:39 ` Greg Nancarrow <[email protected]>
2020-08-14 15:48 ` vignesh C <[email protected]>
2020-08-17 04:14 ` Greg Nancarrow <[email protected]>
2020-08-19 06:21 ` vignesh C <[email protected]>
2020-08-27 02:33 ` Greg Nancarrow <[email protected]>
2020-08-27 02:54 ` Amit Kapila <[email protected]>
2020-08-27 11:26 ` vignesh C <[email protected]>
2020-08-27 12:12 ` Amit Kapila <[email protected]>
2020-08-31 10:43 ` vignesh C <[email protected]>
2020-09-15 13:49 ` Bharath Rupireddy <[email protected]>
2020-09-16 07:50 ` Greg Nancarrow <[email protected]>
2020-09-17 05:36 ` Bharath Rupireddy <[email protected]>
2020-09-22 10:38 ` Bharath Rupireddy <[email protected]>
2020-09-24 02:56 ` Greg Nancarrow <[email protected]>
2020-09-24 07:04 ` Bharath Rupireddy <[email protected]>
2020-10-03 00:49 ` Tomas Vondra <[email protected]>
2020-10-03 10:15 ` Amit Kapila <[email protected]>
2020-10-14 13:29 ` vignesh C <[email protected]>
2020-09-16 13:05 ` Ashutosh Sharma <[email protected]>
2020-09-22 09:14 ` vignesh C <[email protected]>
2020-09-24 09:30 ` Bharath Rupireddy <[email protected]>
2020-09-24 13:38 ` Ashutosh Sharma <[email protected]>
2020-09-28 09:31 ` Amit Kapila <[email protected]>
2020-09-28 13:06 ` Ashutosh Sharma <[email protected]>
2020-10-08 05:45 ` vignesh C <[email protected]>
2020-10-07 18:56 ` vignesh C <[email protected]>
2020-09-29 09:45 ` Greg Nancarrow <[email protected]>
2020-10-01 06:43 ` Amit Kapila <[email protected]>
2020-10-07 18:48 ` vignesh C <[email protected]>
2024-03-04 05:26 Some shared memory chunks are allocated even if related processes won't start Hayato Kuroda (Fujitsu) <[email protected]>
2024-03-04 05:33 ` Re: Some shared memory chunks are allocated even if related processes won't start Tom Lane <[email protected]>
2024-03-04 06:10 ` RE: Some shared memory chunks are allocated even if related processes won't start Hayato Kuroda (Fujitsu) <[email protected]>
2024-03-04 08:09 ` Re: Some shared memory chunks are allocated even if related processes won't start Alvaro Herrera <[email protected]>
2024-03-04 09:50 ` RE: Some shared memory chunks are allocated even if related processes won't start Hayato Kuroda (Fujitsu) <[email protected]>
2024-03-04 11:52 ` Re: Some shared memory chunks are allocated even if related processes won't start 'Alvaro Herrera' <[email protected]>
2024-03-04 13:11 ` RE: Some shared memory chunks are allocated even if related processes won't start Hayato Kuroda (Fujitsu) <[email protected]>
2024-03-04 13:50 ` Re: Some shared memory chunks are allocated even if related processes won't start 'Alvaro Herrera' <[email protected]>
2024-03-05 02:00 ` RE: Some shared memory chunks are allocated even if related processes won't start Hayato Kuroda (Fujitsu) <[email protected]>
2024-03-05 07:34 ` Re: Some shared memory chunks are allocated even if related processes won't start 'Alvaro Herrera' <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-22 15:01 [PATCH v1] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v2] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox